summaryrefslogtreecommitdiffstats
path: root/sys/legacy/dev
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-02-23 21:07:20 +0000
committerthompsa <thompsa@FreeBSD.org>2009-02-23 21:07:20 +0000
commitde29f650e65ee47a3b0430555b28f248cbceb4da (patch)
tree950fd630ee5efc2483a3c62d06f19a6246f5a15c /sys/legacy/dev
parent40c2eac6aa3328ce690020e0325867fd0ee36d1c (diff)
downloadFreeBSD-src-de29f650e65ee47a3b0430555b28f248cbceb4da.zip
FreeBSD-src-de29f650e65ee47a3b0430555b28f248cbceb4da.tar.gz
Move two missed usb drivers out to the graveyard location under sys/legacy/dev.
Diffstat (limited to 'sys/legacy/dev')
-rw-r--r--sys/legacy/dev/ata/ata-usb.c970
-rw-r--r--sys/legacy/dev/sound/usb/uaudio.c4662
-rw-r--r--sys/legacy/dev/sound/usb/uaudio.h56
-rw-r--r--sys/legacy/dev/sound/usb/uaudio_pcm.c481
-rw-r--r--sys/legacy/dev/sound/usb/uaudioreg.h404
5 files changed, 6573 insertions, 0 deletions
diff --git a/sys/legacy/dev/ata/ata-usb.c b/sys/legacy/dev/ata/ata-usb.c
new file mode 100644
index 0000000..5c868b2
--- /dev/null
+++ b/sys/legacy/dev/ata/ata-usb.c
@@ -0,0 +1,970 @@
+/*-
+ * Copyright (c) 2006 - 2008 Søren Schmidt <sos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/ata.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+/* Command Block Wrapper */
+struct bbb_cbw {
+ u_int8_t signature[4];
+#define CBWSIGNATURE 0x43425355
+
+ u_int8_t tag[4];
+ u_int8_t transfer_length[4];
+ u_int8_t flags;
+#define CBWFLAGS_OUT 0x00
+#define CBWFLAGS_IN 0x80
+
+ u_int8_t lun;
+ u_int8_t length;
+#define CBWCDBLENGTH 16
+
+ u_int8_t cdb[CBWCDBLENGTH];
+};
+
+/* Command Status Wrapper */
+struct bbb_csw {
+ u_int8_t signature[4];
+#define CSWSIGNATURE 0x53425355
+
+ u_int8_t tag[4];
+ u_int8_t residue[4];
+ u_int8_t status;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+};
+
+/* USB-ATA 'controller' softc */
+struct atausb_softc {
+ device_t dev; /* base device */
+ usbd_interface_handle iface; /* interface */
+ int ifaceno; /* interface number */
+ u_int8_t bulkin; /* endpoint address's */
+ u_int8_t bulkout;
+ u_int8_t bulkirq;
+ usbd_pipe_handle bulkin_pipe; /* pipe handle's */
+ usbd_pipe_handle bulkout_pipe;
+ usbd_pipe_handle bulkirq_pipe;
+ int maxlun;
+ int timeout;
+ struct ata_request *ata_request;
+ usb_device_request_t usb_request;
+ struct bbb_cbw cbw;
+ struct bbb_csw csw;
+
+#define ATAUSB_T_BBB_CBW 0
+#define ATAUSB_T_BBB_DATA 1
+#define ATAUSB_T_BBB_DCLEAR 2
+#define ATAUSB_T_BBB_CSW1 3
+#define ATAUSB_T_BBB_CSW2 4
+#define ATAUSB_T_BBB_SCLEAR 5
+#define ATAUSB_T_BBB_RESET1 6
+#define ATAUSB_T_BBB_RESET2 7
+#define ATAUSB_T_BBB_RESET3 8
+#define ATAUSB_T_MAX 9
+ usbd_xfer_handle transfer[ATAUSB_T_MAX];
+
+ int state;
+#define ATAUSB_S_ATTACH 0
+#define ATAUSB_S_IDLE 1
+#define ATAUSB_S_BBB_COMMAND 2
+#define ATAUSB_S_BBB_DATA 3
+#define ATAUSB_S_BBB_DCLEAR 4
+#define ATAUSB_S_BBB_STATUS1 5
+#define ATAUSB_S_BBB_SCLEAR 6
+#define ATAUSB_S_BBB_STATUS2 7
+#define ATAUSB_S_BBB_RESET1 8
+#define ATAUSB_S_BBB_RESET2 9
+#define ATAUSB_S_BBB_RESET3 10
+#define ATAUSB_S_DETACH 11
+
+ struct mtx locked_mtx;
+ struct ata_channel *locked_ch;
+ struct ata_channel *restart_ch;
+};
+
+static int atausbdebug = 0;
+
+/* prototypes*/
+static usbd_status atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe, void *buffer, int buflen, int flags, usbd_xfer_handle xfer);
+static usbd_status atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev, usb_device_request_t *req, void *buffer, int buflen, int flags, usbd_xfer_handle xfer);
+static void atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt, usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer);
+static void atausb_bbb_reset(struct atausb_softc *sc);
+static int atausb_bbb_start(struct ata_request *request);
+static void atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err);
+int ata_usbchannel_begin_transaction(struct ata_request *request);
+int ata_usbchannel_end_transaction(struct ata_request *request);
+
+
+/*
+ * USB frontend part
+ */
+USB_DECLARE_DRIVER(atausb);
+DRIVER_MODULE(atausb, uhub, atausb_driver, atausb_devclass, 0, 0);
+MODULE_VERSION(atausb, 1);
+
+static int
+atausb_match(device_t dev)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return UMATCH_NONE;
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ if (!id || id->bInterfaceClass != UICLASS_MASS)
+ return UMATCH_NONE;
+
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_RBC:
+ case UISUBCLASS_SCSI:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ case UISUBCLASS_UFI:
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ case UIPROTO_MASS_CBI_I:
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+ default:
+ return UMATCH_IFACECLASS_IFACESUBCLASS;
+ }
+ break;
+ default:
+ return UMATCH_IFACECLASS;
+ }
+}
+
+static int
+atausb_attach(device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(dev);
+ struct usb_attach_arg *uaa = device_get_ivars(dev);
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_device_handle udev;
+ usb_device_request_t request;
+ device_t child;
+ char devinfo[1024], *proto, *subclass;
+ u_int8_t maxlun;
+ int err, i;
+
+ sc->dev = dev;
+ usbd_devinfo(uaa->device, 0, devinfo);
+ device_set_desc_copy(dev, devinfo);
+ sc->bulkin = sc->bulkout = sc->bulkirq = -1;
+ sc->bulkin_pipe = sc->bulkout_pipe= sc->bulkirq_pipe = NULL;
+ sc->iface = uaa->iface;
+ sc->ifaceno = uaa->ifaceno;
+ sc->maxlun = 0;
+ sc->timeout = 5000;
+ sc->locked_ch = NULL;
+ sc->restart_ch = NULL;
+ mtx_init(&sc->locked_mtx, "ATAUSB lock", NULL, MTX_DEF);
+
+ id = usbd_get_interface_descriptor(sc->iface);
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ proto = "Bulk-Only";
+ break;
+ case UIPROTO_MASS_CBI:
+ proto = "CBI";
+ break;
+ case UIPROTO_MASS_CBI_I:
+ proto = "CBI with CCI";
+ break;
+ default:
+ proto = "Unknown";
+ }
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_RBC:
+ subclass = "RBC";
+ break;
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ subclass = "ATAPI";
+ break;
+ case UISUBCLASS_SCSI:
+ subclass = "SCSI";
+ break;
+ case UISUBCLASS_UFI:
+ subclass = "UFI";
+ break;
+ default:
+ subclass = "Unknown";
+ }
+ device_printf(dev, "using %s over %s\n", subclass, proto);
+ if (strcmp(proto, "Bulk-Only") ||
+ (strcmp(subclass, "ATAPI") && strcmp(subclass, "SCSI")))
+ return ENXIO;
+
+ for (i = 0 ; i < id->bNumEndpoints ; i++) {
+ if (!(ed = usbd_interface2endpoint_descriptor(sc->iface, i))) {
+ device_printf(sc->dev, "could not read endpoint descriptor\n");
+ return ENXIO;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkin = ed->bEndpointAddress;
+ }
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) {
+ sc->bulkout = ed->bEndpointAddress;
+ }
+ if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I &&
+ UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
+ sc->bulkirq = ed->bEndpointAddress;
+ }
+ }
+
+ /* check whether we found at least the endpoints we need */
+ if (!sc->bulkin || !sc->bulkout) {
+ device_printf(sc->dev, "needed endpoints not found (%d,%d)\n",
+ sc->bulkin, sc->bulkout);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+
+ /* open the pipes */
+ if (usbd_open_pipe(sc->iface, sc->bulkout,
+ USBD_EXCLUSIVE_USE, &sc->bulkout_pipe)) {
+ device_printf(sc->dev, "cannot open bulkout pipe (%d)\n", sc->bulkout);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ if (usbd_open_pipe(sc->iface, sc->bulkin,
+ USBD_EXCLUSIVE_USE, &sc->bulkin_pipe)) {
+ device_printf(sc->dev, "cannot open bulkin pipe (%d)\n", sc->bulkin);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I) {
+ if (usbd_open_pipe(sc->iface, sc->bulkirq,
+ USBD_EXCLUSIVE_USE, &sc->bulkirq_pipe)) {
+ device_printf(sc->dev, "cannot open bulkirq pipe (%d)\n",
+ sc->bulkirq);
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ }
+ sc->state = ATAUSB_S_ATTACH;
+
+ /* alloc needed number of transfer handles */
+ for (i = 0; i < ATAUSB_T_MAX; i++) {
+ sc->transfer[i] = usbd_alloc_xfer(uaa->device);
+ if (!sc->transfer[i]) {
+ device_printf(sc->dev, "out of memory\n");
+ atausb_detach(dev);
+ return ENXIO;
+ }
+ }
+
+ /* driver is ready to process requests here */
+ sc->state = ATAUSB_S_IDLE;
+
+ /* get number of devices so we can add matching channels */
+ usbd_interface2device_handle(sc->iface, &udev);
+ request.bmRequestType = UT_READ_CLASS_INTERFACE;
+ request.bRequest = 0xfe; //GET_MAX_LUN;
+ USETW(request.wValue, 0);
+ USETW(request.wIndex, sc->ifaceno);
+ USETW(request.wLength, sizeof(maxlun));
+ switch ((err = usbd_do_request(udev, &request, &maxlun))) {
+ case USBD_NORMAL_COMPLETION:
+ if (bootverbose)
+ device_printf(sc->dev, "maxlun=%d\n", maxlun);
+ sc->maxlun = maxlun;
+ break;
+ default:
+ if (bootverbose)
+ device_printf(sc->dev, "get maxlun not supported %s\n",
+ usbd_errstr(err));
+ }
+
+ /* ata channels are children to this USB control device */
+ for (i = 0; i <= sc->maxlun; i++) {
+ if ((child = device_add_child(sc->dev, "ata",
+ devclass_find_free_unit(ata_devclass, 2))) == NULL) {
+ device_printf(sc->dev, "failed to add ata child device\n");
+ } else
+ device_set_ivars(child, (void *)(intptr_t)i);
+ }
+ bus_generic_attach(sc->dev);
+ return 0;
+}
+
+static int
+atausb_detach(device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(dev);
+ usbd_device_handle udev;
+ device_t *children;
+ int nchildren, i;
+
+ /* signal that device is going away */
+ sc->state = ATAUSB_S_DETACH;
+
+ /* abort all the pipes in case there are active transfers */
+ usbd_interface2device_handle(sc->iface, &udev);
+ usbd_abort_default_pipe(udev);
+ if (sc->bulkout_pipe)
+ usbd_abort_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_abort_pipe(sc->bulkin_pipe);
+ if (sc->bulkirq_pipe)
+ usbd_abort_pipe(sc->bulkirq_pipe);
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+
+ /* free the transfers */
+ for (i = 0; i < ATAUSB_T_MAX; i++)
+ if (sc->transfer[i])
+ usbd_free_xfer(sc->transfer[i]);
+
+ /* remove all the pipes */
+ if (sc->bulkout_pipe)
+ usbd_close_pipe(sc->bulkout_pipe);
+ if (sc->bulkin_pipe)
+ usbd_close_pipe(sc->bulkin_pipe);
+ if (sc->bulkirq_pipe)
+ usbd_close_pipe(sc->bulkirq_pipe);
+
+ mtx_destroy(&sc->locked_mtx);
+ return 0;
+}
+
+
+/*
+ * Generic USB transfer routines
+ */
+static usbd_status
+atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe,
+ void *buffer, int buflen, int flags, usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ if (sc->state == ATAUSB_S_DETACH)
+ return USBD_NOT_STARTED;
+
+ usbd_setup_xfer(xfer, pipe, (void *)sc, buffer, buflen, flags,
+ sc->timeout, atausb_bbb_finish);
+ err = usbd_transfer(xfer);
+ if (err && (err != USBD_IN_PROGRESS)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to setup transfer, %s\n",
+ usbd_errstr(err));
+ return err;
+ }
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev,
+ usb_device_request_t *req, void *buffer, int buflen, int flags,
+ usbd_xfer_handle xfer)
+{
+ usbd_status err;
+
+ if (sc->state == ATAUSB_S_DETACH)
+ return USBD_NOT_STARTED;
+
+ usbd_setup_default_xfer(xfer, udev, (void *)sc, sc->timeout, req,
+ buffer, buflen, flags, atausb_bbb_finish);
+ err = usbd_transfer(xfer);
+ if (err && (err != USBD_IN_PROGRESS)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to setup ctl transfer, %s\n",
+ usbd_errstr(err));
+ return err;
+ }
+ return USBD_NORMAL_COMPLETION;
+}
+
+static void
+atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt,
+ usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer)
+{
+ usbd_device_handle udev;
+
+ if (atausbdebug)
+ device_printf(sc->dev, "clear endpoint 0x%02x stall\n", endpt);
+ usbd_interface2device_handle(sc->iface, &udev);
+ sc->state = state;
+ usbd_clear_endpoint_toggle(pipe);
+ sc->usb_request.bmRequestType = UT_WRITE_ENDPOINT;
+ sc->usb_request.bRequest = UR_CLEAR_FEATURE;
+ USETW(sc->usb_request.wValue, UF_ENDPOINT_HALT);
+ USETW(sc->usb_request.wIndex, endpt);
+ USETW(sc->usb_request.wLength, 0);
+ atausb_ctl_start(sc, udev, &sc->usb_request, NULL, 0, 0, xfer);
+}
+
+
+/*
+ * Bulk-Only transport part
+ */
+static void
+atausb_bbb_reset(struct atausb_softc *sc)
+{
+ usbd_device_handle udev;
+
+ if (atausbdebug)
+ device_printf(sc->dev, "Bulk Reset\n");
+ sc->timeout = 5000;
+ sc->state = ATAUSB_S_BBB_RESET1;
+ usbd_interface2device_handle(sc->iface, &udev);
+ sc->usb_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ sc->usb_request.bRequest = 0xff; /* bulk-only reset */
+ USETW(sc->usb_request.wValue, 0);
+ USETW(sc->usb_request.wIndex, sc->ifaceno);
+ USETW(sc->usb_request.wLength, 0);
+ atausb_ctl_start(sc, udev, &sc->usb_request, NULL,
+ 0, 0, sc->transfer[ATAUSB_T_BBB_RESET1]);
+}
+
+static int
+atausb_bbb_start(struct ata_request *request)
+{
+ struct atausb_softc *sc =
+ device_get_softc(device_get_parent(request->parent));
+ struct ata_channel *ch = device_get_softc(request->parent);
+
+ sc->timeout = (request->timeout * 1000) + 5000;
+ USETDW(sc->cbw.signature, CBWSIGNATURE);
+ USETDW(sc->cbw.tag, UGETDW(sc->cbw.tag) + 1);
+ USETDW(sc->cbw.transfer_length, request->bytecount);
+ sc->cbw.flags = (request->flags & ATA_R_READ) ? CBWFLAGS_IN : CBWFLAGS_OUT;
+ sc->cbw.lun = ch->unit;
+ sc->cbw.length = 16;
+ bzero(sc->cbw.cdb, 16);
+ bcopy(request->u.atapi.ccb, sc->cbw.cdb, 12); /* XXX SOS */
+ sc->state = ATAUSB_S_BBB_COMMAND;
+ if (atausb_start(sc, sc->bulkout_pipe, &sc->cbw, sizeof(struct bbb_cbw),
+ 0, sc->transfer[ATAUSB_T_BBB_CBW])) {
+ request->result = EIO;
+ if (atausbdebug)
+ device_printf(request->dev, "cannot setup USB transfer\n");
+ atausb_bbb_reset(sc);
+ return ATA_OP_FINISHED;
+ }
+ return ATA_OP_CONTINUES;
+}
+
+static void
+atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct atausb_softc *sc = (struct atausb_softc *)priv;
+ struct ata_request *request = sc->ata_request;
+ usbd_xfer_handle next_xfer;
+
+ //device_printf(sc->dev, "BBB state %d: %s\n", sc->state, usbd_errstr(err));
+
+ if (sc->state == ATAUSB_S_DETACH) {
+ device_printf(sc->dev, "WARNING - device has been removed\n");
+ return;
+ }
+
+ switch (sc->state) {
+ case ATAUSB_S_BBB_COMMAND: /* command transport phase */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "failed to send CBW\n");
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+
+ /* next is data transport phase, setup transfer */
+ sc->state = ATAUSB_S_BBB_DATA;
+ if (request->flags & ATA_R_READ) {
+ if (atausb_start(sc, sc->bulkin_pipe,
+ request->data, request->bytecount,
+ USBD_SHORT_XFER_OK,
+ sc->transfer[ATAUSB_T_BBB_DATA])) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ if (request->flags & ATA_R_WRITE) {
+ if (atausb_start(sc, sc->bulkout_pipe,
+ request->data, request->bytecount,
+ 0, sc->transfer[ATAUSB_T_BBB_DATA])) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ /* FALLTHROUGH */
+
+ case ATAUSB_S_BBB_DATA: /* data transport phase */
+ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
+ usbd_get_xfer_status(xfer, NULL, NULL, &request->donecount, NULL);
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "data %s count %d failed: %s\n",
+ (request->flags & ATA_R_READ?"read":"write"),
+ request->bytecount, usbd_errstr(err));
+ if (err == USBD_STALLED) {
+ atausb_clear_stall(sc,
+ (request->flags & ATA_R_READ ?
+ sc->bulkin : sc->bulkout),
+ (request->flags & ATA_R_READ ?
+ sc->bulkin_pipe : sc->bulkout_pipe),
+ ATAUSB_S_BBB_DCLEAR,
+ sc->transfer[ATAUSB_T_BBB_DCLEAR]);
+ }
+ else {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case ATAUSB_S_BBB_DCLEAR: /* stall clear after data phase */
+ case ATAUSB_S_BBB_SCLEAR: /* stall clear after status phase */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bulk%s stall clear failed %s\n",
+ (request->flags & ATA_R_READ ? "in" : "out"),
+ usbd_errstr(err));
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+
+ if (sc->state == ATAUSB_S_BBB_COMMAND ||
+ sc->state == ATAUSB_S_BBB_DATA ||
+ sc->state == ATAUSB_S_BBB_DCLEAR) {
+ /* first attempt on status transport phase setup transfer */
+ sc->state = ATAUSB_S_BBB_STATUS1;
+ next_xfer = sc->transfer[ATAUSB_T_BBB_CSW1];
+ }
+ else {
+ /* second attempt of fetching status */
+ sc->state = ATAUSB_S_BBB_STATUS2;
+ next_xfer = sc->transfer[ATAUSB_T_BBB_CSW2];
+ }
+ if (atausb_start(sc, sc->bulkin_pipe, &sc->csw, sizeof(struct bbb_csw),
+ USBD_SHORT_XFER_OK, next_xfer)) {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+
+ case ATAUSB_S_BBB_STATUS1: /* status transfer first attempt */
+ case ATAUSB_S_BBB_STATUS2: /* status transfer second attempt */
+ if (err) {
+ if (atausbdebug)
+ device_printf(sc->dev, "cannot get CSW, %s%s\n",
+ usbd_errstr(err),
+ sc->state == ATAUSB_S_BBB_STATUS1 ? ", retry":"");
+ if (sc->state == ATAUSB_S_BBB_STATUS1) {
+ atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe,
+ ATAUSB_S_BBB_SCLEAR,
+ sc->transfer[ATAUSB_T_BBB_SCLEAR]);
+ }
+ else {
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ }
+ return;
+ }
+
+ int residue = UGETDW(sc->csw.residue);
+
+ if (!residue &&
+ (request->bytecount - request->donecount))
+ residue = request->bytecount - request->donecount;
+
+ /* check CSW and handle eventual error */
+ if (UGETDW(sc->csw.signature) != CSWSIGNATURE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW signature 0x%08x != 0x%08x\n",
+ UGETDW(sc->csw.signature), CSWSIGNATURE);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (UGETDW(sc->csw.tag) != UGETDW(sc->cbw.tag)) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW tag %d != %d\n",
+ UGETDW(sc->csw.tag), UGETDW(sc->cbw.tag));
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status > CSWSTATUS_PHASE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "bad CSW status %d > %d\n",
+ sc->csw.status, CSWSTATUS_PHASE);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status == CSWSTATUS_PHASE) {
+ if (atausbdebug)
+ device_printf(sc->dev, "phase error residue = %d\n", residue);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (request->donecount > request->bytecount) {
+ if (atausbdebug)
+ device_printf(sc->dev, "buffer overrun %d > %d",
+ request->donecount, request->bytecount);
+ request->result = EIO;
+ atausb_bbb_reset(sc);
+ return;
+ }
+ else if (sc->csw.status == CSWSTATUS_FAILED) {
+ if (atausbdebug)
+ device_printf(sc->dev, "CSWSTATUS_FAILED\n");
+ request->error = ATA_E_ATAPI_SENSE_MASK ;
+ sc->state = ATAUSB_S_IDLE;
+ ata_interrupt(device_get_softc(request->parent));
+ return;
+ }
+ else {
+ sc->state = ATAUSB_S_IDLE;
+ ata_interrupt(device_get_softc(request->parent));
+ return;
+ }
+ /* NOT REACHED */
+
+ case ATAUSB_S_BBB_RESET1:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev,
+ "BBB reset failure: %s\n", usbd_errstr(err));
+ atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe,
+ ATAUSB_S_BBB_RESET2,
+ sc->transfer[ATAUSB_T_BBB_RESET2]);
+ return;
+
+ case ATAUSB_S_BBB_RESET2:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev, "BBB bulkin clear stall failure: %s\n",
+ usbd_errstr(err));
+ atausb_clear_stall(sc, sc->bulkout, sc->bulkout_pipe,
+ ATAUSB_S_BBB_RESET3,
+ sc->transfer[ATAUSB_T_BBB_RESET3]);
+ return;
+
+ case ATAUSB_S_BBB_RESET3:
+ if (err)
+ if (atausbdebug)
+ device_printf(sc->dev, "BBB bulk-out clear stall failure: %s\n",
+ usbd_errstr(err));
+ sc->state = ATAUSB_S_IDLE;
+ if (request) {
+ if (err)
+ request->result = ENXIO;
+ else
+ request->result = EIO;
+ ata_interrupt(device_get_softc(request->parent));
+ }
+ return;
+
+ default:
+ if (atausbdebug)
+ device_printf(sc->dev, "unknown state %d", sc->state);
+ }
+}
+
+
+/*
+ * ATA backend part
+ */
+struct atapi_inquiry {
+ u_int8_t device_type;
+ u_int8_t device_modifier;
+ u_int8_t version;
+ u_int8_t response_format;
+ u_int8_t length;
+ u_int8_t reserved[2];
+ u_int8_t flags;
+ u_int8_t vendor[8];
+ u_int8_t product[16];
+ u_int8_t revision[4];
+ //u_int8_t crap[60];
+};
+
+int
+ata_usbchannel_begin_transaction(struct ata_request *request)
+{
+ struct atausb_softc *sc =
+ device_get_softc(device_get_parent(request->parent));
+
+ if (atausbdebug > 1)
+ device_printf(request->dev, "begin_transaction %s\n",
+ ata_cmd2str(request));
+
+ /* sanity just in case */
+ if (sc->state != ATAUSB_S_IDLE) {
+ printf("begin is busy (%d)\n", sc->state);
+ request->result = EBUSY;
+ return ATA_OP_FINISHED;
+ }
+
+ /* XXX SOS convert the request into the format used, only BBB for now*/
+ sc->ata_request = request;
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if (!(request->flags & ATA_R_ATAPI)) {
+ if (request->u.ata.command != ATA_ATAPI_IDENTIFY) {
+ device_printf(request->dev,"%s unsupported\n",ata_cmd2str(request));
+ request->result = EIO;
+ return ATA_OP_FINISHED;
+ }
+ request->flags |= ATA_R_ATAPI;
+ bzero(request->u.atapi.ccb, 16);
+ request->u.atapi.ccb[0] = ATAPI_INQUIRY;
+ request->u.atapi.ccb[4] = 255; //sizeof(struct atapi_inquiry);
+ request->data += 256; /* arbitrary offset into ata_param */
+ request->bytecount = 255; //sizeof(struct atapi_inquiry);
+ }
+ return atausb_bbb_start(request);
+}
+
+int
+ata_usbchannel_end_transaction(struct ata_request *request)
+{
+ if (atausbdebug > 1)
+ device_printf(request->dev, "end_transaction %s\n",
+ ata_cmd2str(request));
+
+ /* XXX SOS convert the request from the format used, only BBB for now*/
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if ((request->flags & ATA_R_ATAPI) &&
+ (request->u.atapi.ccb[0] == ATAPI_INQUIRY)) {
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct atapi_inquiry *inquiry = (struct atapi_inquiry *)request->data;
+ u_int16_t *ptr;
+
+ /* convert inquiry data into simple ata_param like format */
+ atadev->param.config = ATA_PROTO_ATAPI | ATA_PROTO_ATAPI_12;
+ atadev->param.config |= (inquiry->device_type & 0x1f) << 8;
+ bzero(atadev->param.model, sizeof(atadev->param.model));
+ strncpy(atadev->param.model, inquiry->vendor, 8);
+ strcpy(atadev->param.model, " ");
+ strncpy(atadev->param.model, inquiry->product, 16);
+ ptr = (u_int16_t*)(atadev->param.model + sizeof(atadev->param.model));
+ while (--ptr >= (u_int16_t*)atadev->param.model)
+ *ptr = ntohs(*ptr);
+ strncpy(atadev->param.revision, inquiry->revision, 4);
+ ptr=(u_int16_t*)(atadev->param.revision+sizeof(atadev->param.revision));
+ while (--ptr >= (u_int16_t*)atadev->param.revision)
+ *ptr = ntohs(*ptr);
+ request->result = 0;
+ }
+ return ATA_OP_FINISHED;
+}
+
+static int
+ata_usbchannel_probe(device_t dev)
+{
+ char buffer[32];
+
+ sprintf(buffer, "USB lun %d", (int)(intptr_t)device_get_ivars(dev));
+ device_set_desc_copy(dev, buffer);
+
+ return 0;
+}
+
+static int
+ata_usbchannel_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (ch->attached)
+ return (0);
+ ch->attached = 1;
+
+ /* initialize the softc basics */
+ ch->dev = dev;
+ ch->unit = (intptr_t)device_get_ivars(dev);
+ ch->state = ATA_IDLE;
+ ch->hw.begin_transaction = ata_usbchannel_begin_transaction;
+ ch->hw.end_transaction = ata_usbchannel_end_transaction;
+ ch->hw.status = NULL;
+ ch->hw.command = NULL;
+ bzero(&ch->state_mtx, sizeof(struct mtx));
+ mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF);
+ bzero(&ch->queue_mtx, sizeof(struct mtx));
+ mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF);
+ TAILQ_INIT(&ch->ata_queue);
+
+ /* XXX SOS reset the controller HW, the channel and device(s) */
+ //ATA_RESET(dev);
+
+ /* probe and attach device on this channel */
+ ch->devices = ATA_ATAPI_MASTER;
+ if (!ata_delayed_attach)
+ ata_identify(dev);
+ return 0;
+}
+
+static int
+ata_usbchannel_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ if (!ch->attached)
+ return (0);
+ ch->attached = 0;
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+ mtx_destroy(&ch->state_mtx);
+ mtx_destroy(&ch->queue_mtx);
+ return 0;
+}
+
+static void
+ata_usbchannel_setmode(device_t parent, device_t dev)
+{
+ struct atausb_softc *sc = device_get_softc(GRANDPARENT(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+ usbd_device_handle udev;
+
+ usbd_interface2device_handle(sc->iface, &udev);
+ if (usbd_get_speed(udev) == USB_SPEED_HIGH)
+ atadev->mode = ATA_USB2;
+ else
+ atadev->mode = ATA_USB1;
+}
+
+static int
+ata_usbchannel_locking(device_t dev, int flags)
+{
+ struct atausb_softc *sc = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int res = -1;
+
+
+ mtx_lock(&sc->locked_mtx);
+ switch (flags) {
+ case ATA_LF_LOCK:
+ if (sc->locked_ch == NULL)
+ sc->locked_ch = ch;
+ if (sc->locked_ch != ch)
+ sc->restart_ch = ch;
+ break;
+
+ case ATA_LF_UNLOCK:
+ if (sc->locked_ch == ch) {
+ sc->locked_ch = NULL;
+ if (sc->restart_ch) {
+ ch = sc->restart_ch;
+ sc->restart_ch = NULL;
+ mtx_unlock(&sc->locked_mtx);
+ ata_start(ch->dev);
+ return res;
+ }
+ }
+ break;
+
+ case ATA_LF_WHICH:
+ break;
+ }
+ if (sc->locked_ch)
+ res = sc->locked_ch->unit;
+ mtx_unlock(&sc->locked_mtx);
+ return res;
+}
+
+static device_method_t ata_usbchannel_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_usbchannel_probe),
+ DEVMETHOD(device_attach, ata_usbchannel_attach),
+ DEVMETHOD(device_detach, ata_usbchannel_detach),
+
+ /* ATA methods */
+ DEVMETHOD(ata_setmode, ata_usbchannel_setmode),
+ DEVMETHOD(ata_locking, ata_usbchannel_locking),
+ //DEVMETHOD(ata_reset, ata_usbchannel_reset),
+
+ { 0, 0 }
+};
+
+static driver_t ata_usbchannel_driver = {
+ "ata",
+ ata_usbchannel_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, atausb, ata_usbchannel_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(atausb, ata, 1, 1, 1);
diff --git a/sys/legacy/dev/sound/usb/uaudio.c b/sys/legacy/dev/sound/usb/uaudio.c
new file mode 100644
index 0000000..758d6cd
--- /dev/null
+++ b/sys/legacy/dev/sound/usb/uaudio.c
@@ -0,0 +1,4662 @@
+/* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
+ * http://www.usb.org/developers/devclass_docs/frmts10.pdf
+ * http://www.usb.org/developers/devclass_docs/termt10.pdf
+ */
+
+#include <sys/cdefs.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $");
+#endif
+
+/*
+ * Also merged:
+ * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $
+ * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $
+ * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $
+ * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $
+ * $NetBSD: uaudio.c,v 1.102 2006/04/14 17:00:55 christos Exp $
+ * $NetBSD: uaudio.c,v 1.103 2006/05/11 19:09:25 mrg Exp $
+ * $NetBSD: uaudio.c,v 1.105 2006/10/04 16:00:15 christos Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/reboot.h> /* for bootverbose */
+#include <sys/select.h>
+#include <sys/proc.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/device.h>
+#elif defined(__FreeBSD__)
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#endif
+#include <sys/poll.h>
+#if defined(__FreeBSD__)
+#include <sys/sysctl.h>
+#include <sys/sbuf.h>
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+#include <dev/audiovar.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+#elif defined(__FreeBSD__)
+#include <dev/sound/pcm/sound.h> /* XXXXX */
+#include <dev/sound/chip.h>
+#include "feeder_if.h"
+#endif
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_quirks.h>
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <dev/usb/uaudioreg.h>
+#elif defined(__FreeBSD__)
+#include <dev/sound/usb/uaudioreg.h>
+#include <dev/sound/usb/uaudio.h>
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/* #define UAUDIO_DEBUG */
+#else
+/* #define USB_DEBUG */
+#endif
+/* #define UAUDIO_MULTIPLE_ENDPOINTS */
+#ifdef USB_DEBUG
+#define DPRINTF(x) do { if (uaudiodebug) printf x; } while (0)
+#define DPRINTFN(n,x) do { if (uaudiodebug>(n)) printf x; } while (0)
+int uaudiodebug = 0;
+#if defined(__FreeBSD__)
+SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
+SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
+ &uaudiodebug, 0, "uaudio debug level");
+#endif
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UAUDIO_NCHANBUFS 6 /* number of outstanding request */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define UAUDIO_NFRAMES 10 /* ms of sound in each request */
+#elif defined(__FreeBSD__)
+#define UAUDIO_NFRAMES 20 /* ms of sound in each request */
+#endif
+
+
+#define MIX_MAX_CHAN 8
+struct mixerctl {
+ uint16_t wValue[MIX_MAX_CHAN]; /* using nchan */
+ uint16_t wIndex;
+ uint8_t nchan;
+ uint8_t type;
+#define MIX_ON_OFF 1
+#define MIX_SIGNED_16 2
+#define MIX_UNSIGNED_16 3
+#define MIX_SIGNED_8 4
+#define MIX_SELECTOR 5
+#define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1)
+#define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
+ int minval, maxval;
+ u_int delta;
+ u_int mul;
+#if defined(__FreeBSD__) /* XXXXX */
+ unsigned ctl;
+#define MAX_SELECTOR_INPUT_PIN 256
+ uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN];
+#endif
+ uint8_t class;
+#if !defined(__FreeBSD__)
+ char ctlname[MAX_AUDIO_DEV_LEN];
+ char *ctlunit;
+#endif
+};
+#define MAKE(h,l) (((h) << 8) | (l))
+
+struct as_info {
+ uint8_t alt;
+ uint8_t encoding;
+ uint8_t attributes; /* Copy of bmAttributes of
+ * usb_audio_streaming_endpoint_descriptor
+ */
+ usbd_interface_handle ifaceh;
+ const usb_interface_descriptor_t *idesc;
+ const usb_endpoint_descriptor_audio_t *edesc;
+ const usb_endpoint_descriptor_audio_t *edesc1;
+ const struct usb_audio_streaming_type1_descriptor *asf1desc;
+ int sc_busy; /* currently used */
+};
+
+struct chan {
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ void (*intr)(void *); /* DMA completion intr handler */
+ void *arg; /* arg for intr() */
+#else
+ struct pcm_channel *pcm_ch;
+#endif
+ usbd_pipe_handle pipe;
+ usbd_pipe_handle sync_pipe;
+
+ u_int sample_size;
+ u_int sample_rate;
+ u_int bytes_per_frame;
+ u_int fraction; /* fraction/1000 is the extra samples/frame */
+ u_int residue; /* accumulates the fractional samples */
+
+ u_char *start; /* upper layer buffer start */
+ u_char *end; /* upper layer buffer end */
+ u_char *cur; /* current position in upper layer buffer */
+ int blksize; /* chunk size to report up */
+ int transferred; /* transferred bytes not reported up */
+
+ int altidx; /* currently used altidx */
+
+ int curchanbuf;
+ struct chanbuf {
+ struct chan *chan;
+ usbd_xfer_handle xfer;
+ u_char *buffer;
+ u_int16_t sizes[UAUDIO_NFRAMES];
+ u_int16_t offsets[UAUDIO_NFRAMES];
+ u_int16_t size;
+ } chanbufs[UAUDIO_NCHANBUFS];
+
+ struct uaudio_softc *sc; /* our softc */
+#if defined(__FreeBSD__)
+ u_int32_t format;
+ int precision;
+ int channels;
+#endif
+};
+
+struct uaudio_softc {
+ device_t sc_dev; /* base device */
+ usbd_device_handle sc_udev; /* USB device */
+ int sc_ac_iface; /* Audio Control interface */
+ usbd_interface_handle sc_ac_ifaceh;
+ struct chan sc_playchan; /* play channel */
+ struct chan sc_recchan; /* record channel */
+ int sc_nullalt;
+ int sc_audio_rev;
+ struct as_info *sc_alts; /* alternate settings */
+ int sc_nalts; /* # of alternate settings */
+ int sc_altflags;
+#define HAS_8 0x01
+#define HAS_16 0x02
+#define HAS_8U 0x04
+#define HAS_ALAW 0x08
+#define HAS_MULAW 0x10
+#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */
+#define HAS_24 0x40
+#define HAS_32 0x80
+ int sc_mode; /* play/record capability */
+ struct mixerctl *sc_ctls; /* mixer controls */
+ int sc_nctls; /* # of mixer controls */
+ device_t sc_audiodev;
+ char sc_dying;
+#if defined(__FreeBSD__)
+ struct sbuf uaudio_sndstat;
+ int uaudio_sndstat_flag;
+ int async;
+#endif
+ int sc_vendor;
+ int sc_product;
+ int sc_release;
+};
+
+struct terminal_list {
+ int size;
+ uint16_t terminals[1];
+};
+#define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \
+ + sizeof(uint16_t) * (N))
+
+struct io_terminal {
+ union {
+ const usb_descriptor_t *desc;
+ const struct usb_audio_input_terminal *it;
+ const struct usb_audio_output_terminal *ot;
+ const struct usb_audio_mixer_unit *mu;
+ const struct usb_audio_selector_unit *su;
+ const struct usb_audio_feature_unit *fu;
+ const struct usb_audio_processing_unit *pu;
+ const struct usb_audio_extension_unit *eu;
+ } d;
+ int inputs_size;
+ struct terminal_list **inputs; /* list of source input terminals */
+ struct terminal_list *output; /* list of destination output terminals */
+ int direct; /* directly connected to an output terminal */
+};
+
+#define UAC_OUTPUT 0
+#define UAC_INPUT 1
+#define UAC_EQUAL 2
+#define UAC_RECORD 3
+#define UAC_NCLASSES 4
+#ifdef USB_DEBUG
+#if defined(__FreeBSD__)
+#define AudioCinputs "inputs"
+#define AudioCoutputs "outputs"
+#define AudioCrecord "record"
+#define AudioCequalization "equalization"
+#endif
+static const char *uac_names[] = {
+ AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord,
+};
+#endif
+
+static usbd_status uaudio_identify_ac
+ (struct uaudio_softc *, const usb_config_descriptor_t *);
+static usbd_status uaudio_identify_as
+ (struct uaudio_softc *, const usb_config_descriptor_t *);
+static usbd_status uaudio_process_as
+ (struct uaudio_softc *, const char *, int *, int,
+ const usb_interface_descriptor_t *);
+
+static void uaudio_add_alt(struct uaudio_softc *, const struct as_info *);
+
+static const usb_interface_descriptor_t *uaudio_find_iface
+ (const char *, int, int *, int);
+
+static void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static char *uaudio_id_name
+ (struct uaudio_softc *, const struct io_terminal *, int);
+#endif
+
+#ifdef USB_DEBUG
+static void uaudio_dump_cluster(const struct usb_audio_cluster *);
+#endif
+static struct usb_audio_cluster uaudio_get_cluster
+ (int, const struct io_terminal *);
+static void uaudio_add_input
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_output
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_mixer
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_selector
+ (struct uaudio_softc *, const struct io_terminal *, int);
+#ifdef USB_DEBUG
+static const char *uaudio_get_terminal_name(int);
+#endif
+static int uaudio_determine_class
+ (const struct io_terminal *, struct mixerctl *);
+#if defined(__FreeBSD__)
+static int uaudio_feature_name(const struct io_terminal *,
+ struct mixerctl *);
+#else
+static const char *uaudio_feature_name
+ (const struct io_terminal *, struct mixerctl *);
+#endif
+static void uaudio_add_feature
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_processing_updown
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_processing
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static void uaudio_add_extension
+ (struct uaudio_softc *, const struct io_terminal *, int);
+static struct terminal_list *uaudio_merge_terminal_list
+ (const struct io_terminal *);
+static struct terminal_list *uaudio_io_terminaltype
+ (int, struct io_terminal *, int);
+static usbd_status uaudio_identify
+ (struct uaudio_softc *, const usb_config_descriptor_t *);
+
+static int uaudio_signext(int, int);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int uaudio_value2bsd(struct mixerctl *, int);
+#endif
+static int uaudio_bsd2value(struct mixerctl *, int);
+static int uaudio_get(struct uaudio_softc *, int, int, int, int, int);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int uaudio_ctl_get
+ (struct uaudio_softc *, int, struct mixerctl *, int);
+#endif
+static void uaudio_set
+ (struct uaudio_softc *, int, int, int, int, int, int);
+static void uaudio_ctl_set
+ (struct uaudio_softc *, int, struct mixerctl *, int, int);
+
+static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int);
+
+static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *);
+static void uaudio_chan_close(struct uaudio_softc *, struct chan *);
+static usbd_status uaudio_chan_alloc_buffers
+ (struct uaudio_softc *, struct chan *);
+static void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static void uaudio_chan_init
+ (struct chan *, int, const struct audio_params *, int);
+static void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
+#endif
+
+static void uaudio_chan_ptransfer(struct chan *);
+static void uaudio_chan_pintr
+ (usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+static void uaudio_chan_rtransfer(struct chan *);
+static void uaudio_chan_rintr
+ (usbd_xfer_handle, usbd_private_handle, usbd_status);
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int uaudio_open(void *, int);
+static void uaudio_close(void *);
+static int uaudio_drain(void *);
+static int uaudio_query_encoding(void *, struct audio_encoding *);
+static void uaudio_get_minmax_rates
+ (int, const struct as_info *, const struct audio_params *,
+ int, u_long *, u_long *);
+static int uaudio_match_alt_sub
+ (int, const struct as_info *, const struct audio_params *, int, u_long);
+static int uaudio_match_alt_chan
+ (int, const struct as_info *, struct audio_params *, int);
+static int uaudio_match_alt
+ (int, const struct as_info *, struct audio_params *, int);
+static int uaudio_set_params
+ (void *, int, int, struct audio_params *, struct audio_params *);
+static int uaudio_round_blocksize(void *, int);
+static int uaudio_trigger_output
+ (void *, void *, void *, int, void (*)(void *), void *,
+ struct audio_params *);
+static int uaudio_trigger_input
+ (void *, void *, void *, int, void (*)(void *), void *,
+ struct audio_params *);
+static int uaudio_halt_in_dma(void *);
+static int uaudio_halt_out_dma(void *);
+static int uaudio_getdev(void *, struct audio_device *);
+static int uaudio_mixer_set_port(void *, mixer_ctrl_t *);
+static int uaudio_mixer_get_port(void *, mixer_ctrl_t *);
+static int uaudio_query_devinfo(void *, mixer_devinfo_t *);
+static int uaudio_get_props(void *);
+
+static const struct audio_hw_if uaudio_hw_if = {
+ uaudio_open,
+ uaudio_close,
+ uaudio_drain,
+ uaudio_query_encoding,
+ uaudio_set_params,
+ uaudio_round_blocksize,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ uaudio_halt_out_dma,
+ uaudio_halt_in_dma,
+ NULL,
+ uaudio_getdev,
+ NULL,
+ uaudio_mixer_set_port,
+ uaudio_mixer_get_port,
+ uaudio_query_devinfo,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ uaudio_get_props,
+ uaudio_trigger_output,
+ uaudio_trigger_input,
+ NULL,
+};
+
+static struct audio_device uaudio_device = {
+ "USB audio",
+ "",
+ "uaudio"
+};
+
+#elif defined(__FreeBSD__)
+static int audio_attach_mi(device_t);
+static int uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode);
+static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
+
+/* for NetBSD compatibirity */
+#define AUMODE_PLAY 0x01
+#define AUMODE_RECORD 0x02
+
+#define AUDIO_PROP_FULLDUPLEX 0x01
+
+#define AUDIO_ENCODING_ULAW 1
+#define AUDIO_ENCODING_ALAW 2
+#define AUDIO_ENCODING_SLINEAR_LE 6
+#define AUDIO_ENCODING_SLINEAR_BE 7
+#define AUDIO_ENCODING_ULINEAR_LE 8
+#define AUDIO_ENCODING_ULINEAR_BE 9
+
+#endif /* FreeBSD */
+
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+
+USB_DECLARE_DRIVER(uaudio);
+
+#elif defined(__FreeBSD__)
+
+USB_DECLARE_DRIVER_INIT(uaudio,
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(bus_print_child, bus_generic_print_child)
+ );
+#endif
+
+
+USB_MATCH(uaudio)
+{
+ USB_MATCH_START(uaudio, uaa);
+ usb_interface_descriptor_t *id;
+
+ if (uaa->iface == NULL)
+ return UMATCH_NONE;
+
+ id = usbd_get_interface_descriptor(uaa->iface);
+ /* Trigger on the control interface. */
+ if (id == NULL ||
+ id->bInterfaceClass != UICLASS_AUDIO ||
+ id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL ||
+ (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO))
+ return UMATCH_NONE;
+
+ return UMATCH_IFACECLASS_IFACESUBCLASS;
+}
+
+USB_ATTACH(uaudio)
+{
+ USB_ATTACH_START(uaudio, sc, uaa);
+ usb_interface_descriptor_t *id;
+ usb_config_descriptor_t *cdesc;
+#if !defined(__FreeBSD__)
+ char devinfo[1024];
+#endif
+ usbd_status err;
+ int i, j, found;
+
+#if defined(__FreeBSD__)
+ sc->sc_dev = self;
+#else
+ usbd_devinfo(uaa->device, 0, devinfo, sizeof(devinfo));
+ printf(": %s\n", devinfo);
+#endif
+
+ sc->sc_udev = uaa->device;
+ sc->sc_vendor = uaa->vendor;
+ sc->sc_product = uaa->product;
+ sc->sc_release = uaa->release;
+#if defined(__FreeBSD__)
+ if (resource_int_value(device_get_name(sc->sc_dev),
+ device_get_unit(sc->sc_dev), "async", &i) == 0 && i != 0)
+ sc->async = 1;
+ else
+ sc->async = 0;
+#endif
+
+ cdesc = usbd_get_config_descriptor(sc->sc_udev);
+ if (cdesc == NULL) {
+ printf("%s: failed to get configuration descriptor\n",
+ device_get_nameunit(sc->sc_dev));
+ return ENXIO;
+ }
+
+ err = uaudio_identify(sc, cdesc);
+ if (err) {
+ printf("%s: audio descriptors make no sense, error=%d\n",
+ device_get_nameunit(sc->sc_dev), err);
+ return ENXIO;
+ }
+
+ sc->sc_ac_ifaceh = uaa->iface;
+ /* Pick up the AS interface. */
+ for (i = 0; i < uaa->nifaces; i++) {
+ if (uaa->ifaces[i] == NULL)
+ continue;
+ id = usbd_get_interface_descriptor(uaa->ifaces[i]);
+ if (id == NULL)
+ continue;
+ found = 0;
+ for (j = 0; j < sc->sc_nalts; j++) {
+ if (id->bInterfaceNumber ==
+ sc->sc_alts[j].idesc->bInterfaceNumber) {
+ sc->sc_alts[j].ifaceh = uaa->ifaces[i];
+ found = 1;
+ }
+ }
+ if (found)
+ uaa->ifaces[i] = NULL;
+ }
+
+ for (j = 0; j < sc->sc_nalts; j++) {
+ if (sc->sc_alts[j].ifaceh == NULL) {
+ printf("%s: alt %d missing AS interface(s)\n",
+ device_get_nameunit(sc->sc_dev), j);
+ return ENXIO;
+ }
+ }
+
+ printf("%s: audio rev %d.%02x\n", device_get_nameunit(sc->sc_dev),
+ sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff);
+
+ sc->sc_playchan.sc = sc->sc_recchan.sc = sc;
+ sc->sc_playchan.altidx = -1;
+ sc->sc_recchan.altidx = -1;
+
+ if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC)
+ sc->sc_altflags |= UA_NOFRAC;
+
+#ifndef USB_DEBUG
+ if (bootverbose)
+#endif
+ printf("%s: %d mixer controls\n", device_get_nameunit(sc->sc_dev),
+ sc->sc_nctls);
+
+#if !defined(__FreeBSD__)
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+#endif
+
+ DPRINTF(("uaudio_attach: doing audio_attach_mi\n"));
+#if defined(__OpenBSD__)
+ audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev);
+#elif defined(__NetBSD__)
+ sc->sc_audiodev = audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev);
+#elif defined(__FreeBSD__)
+ sc->sc_dying = 0;
+ if (audio_attach_mi(sc->sc_dev)) {
+ printf("audio_attach_mi failed\n");
+ return ENXIO;
+ }
+#endif
+
+#if defined(__FreeBSD__)
+ SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->sc_dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
+ OID_AUTO, "async", CTLFLAG_RW, &sc->async, 0,
+ "Asynchronous USB request");
+#endif
+ return 0;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uaudio_activate(device_t self, enum devact act)
+{
+ struct uaudio_softc *sc;
+ int rv;
+
+ sc = (struct uaudio_softc *)self;
+ rv = 0;
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return EOPNOTSUPP;
+
+ case DVACT_DEACTIVATE:
+ if (sc->sc_audiodev != NULL)
+ rv = config_deactivate(sc->sc_audiodev);
+ sc->sc_dying = 1;
+ break;
+ }
+ return rv;
+}
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int
+uaudio_detach(device_t self, int flags)
+{
+ struct uaudio_softc *sc;
+ int rv;
+
+ sc = (struct uaudio_softc *)self;
+ rv = 0;
+ /* Wait for outstanding requests to complete. */
+ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES);
+
+ if (sc->sc_audiodev != NULL)
+ rv = config_detach(sc->sc_audiodev, flags);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return rv;
+}
+#elif defined(__FreeBSD__)
+
+USB_DETACH(uaudio)
+{
+ struct sndcard_func *func;
+ device_t *devlist = NULL;
+ int err, i, devcount;
+
+ USB_DETACH_START(uaudio, sc);
+
+ sbuf_delete(&(sc->uaudio_sndstat));
+ sc->uaudio_sndstat_flag = 0;
+
+ sc->sc_dying = 1;
+
+#if 0 /* XXX */
+ /* Wait for outstanding requests to complete. */
+ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES);
+#endif
+
+ err = bus_generic_detach(sc->sc_dev);
+
+ if (err == 0 &&
+ device_get_children(sc->sc_dev, &devlist, &devcount) == 0) {
+ for (i = 0; devlist != NULL && i < devcount; i++) {
+ func = device_get_ivars(devlist[i]);
+ if (func != NULL && func->func == SCF_PCM &&
+ func->varinfo == NULL) {
+ device_set_ivars(devlist[i], NULL);
+ free(func, M_DEVBUF);
+ device_delete_child(sc->sc_dev, devlist[i]);
+ }
+ }
+ free(devlist, M_TEMP);
+ }
+
+ return err;
+}
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int
+uaudio_query_encoding(void *addr, struct audio_encoding *fp)
+{
+ struct uaudio_softc *sc;
+ int flags;
+ int idx;
+
+ sc = addr;
+ flags = sc->sc_altflags;
+ if (sc->sc_dying)
+ return EIO;
+
+ if (sc->sc_nalts == 0 || flags == 0)
+ return ENXIO;
+
+ idx = fp->index;
+ switch (idx) {
+ case 0:
+ strlcpy(fp->name, AudioEulinear, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_ULINEAR;
+ fp->precision = 8;
+ fp->flags = flags&HAS_8U ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 1:
+ strlcpy(fp->name, AudioEmulaw, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_ULAW;
+ fp->precision = 8;
+ fp->flags = flags&HAS_MULAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 2:
+ strlcpy(fp->name, AudioEalaw, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_ALAW;
+ fp->precision = 8;
+ fp->flags = flags&HAS_ALAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 3:
+ strlcpy(fp->name, AudioEslinear, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_SLINEAR;
+ fp->precision = 8;
+ fp->flags = flags&HAS_8 ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 4:
+ strlcpy(fp->name, AudioEslinear_le, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ fp->precision = 16;
+ fp->flags = 0;
+ return (0);
+ case 5:
+ strlcpy(fp->name, AudioEulinear_le, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 6:
+ strlcpy(fp->name, AudioEslinear_be, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ case 7:
+ strlcpy(fp->name, AudioEulinear_be, sizeof(fp->name));
+ fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+#endif
+
+static const usb_interface_descriptor_t *
+uaudio_find_iface(const char *buf, int size, int *offsp, int subtype)
+{
+ const usb_interface_descriptor_t *d;
+
+ while (*offsp < size) {
+ d = (const void *)(buf + *offsp);
+ *offsp += d->bLength;
+ if (d->bDescriptorType == UDESC_INTERFACE &&
+ d->bInterfaceClass == UICLASS_AUDIO &&
+ d->bInterfaceSubClass == subtype)
+ return d;
+ }
+ return NULL;
+}
+
+static void
+uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc)
+{
+ int res;
+ size_t len;
+ struct mixerctl *nmc;
+
+#if defined(__FreeBSD__)
+ if (mc->class < UAC_NCLASSES) {
+ DPRINTF(("%s: adding %s.%d\n",
+ __func__, uac_names[mc->class], mc->ctl));
+ } else {
+ DPRINTF(("%s: adding %d\n", __func__, mc->ctl));
+ }
+#else
+ if (mc->class < UAC_NCLASSES) {
+ DPRINTF(("%s: adding %s.%s\n",
+ __func__, uac_names[mc->class], mc->ctlname));
+ } else {
+ DPRINTF(("%s: adding %s\n", __func__, mc->ctlname));
+ }
+#endif
+ len = sizeof(*mc) * (sc->sc_nctls + 1);
+ nmc = malloc(len, M_USBDEV, M_NOWAIT);
+ if (nmc == NULL) {
+ printf("uaudio_mixer_add_ctl: no memory\n");
+ return;
+ }
+ /* Copy old data, if there was any */
+ if (sc->sc_nctls != 0) {
+ memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls));
+ free(sc->sc_ctls, M_USBDEV);
+ }
+ sc->sc_ctls = nmc;
+
+ mc->delta = 0;
+ if (mc->type == MIX_ON_OFF) {
+ mc->minval = 0;
+ mc->maxval = 1;
+ } else if (mc->type == MIX_SELECTOR) {
+ ;
+ } else {
+ /* Determine min and max values. */
+ mc->minval = uaudio_signext(mc->type,
+ uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE,
+ mc->wValue[0], mc->wIndex,
+ MIX_SIZE(mc->type)));
+ mc->maxval = 1 + uaudio_signext(mc->type,
+ uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE,
+ mc->wValue[0], mc->wIndex,
+ MIX_SIZE(mc->type)));
+ mc->mul = mc->maxval - mc->minval;
+ if (mc->mul == 0)
+ mc->mul = 1;
+ res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE,
+ mc->wValue[0], mc->wIndex,
+ MIX_SIZE(mc->type));
+ if (res > 0)
+ mc->delta = (res * 255 + mc->mul/2) / mc->mul;
+ }
+
+ sc->sc_ctls[sc->sc_nctls++] = *mc;
+
+#ifdef USB_DEBUG
+ if (uaudiodebug > 2) {
+ int i;
+ DPRINTF(("uaudio_mixer_add_ctl: wValue=%04x",mc->wValue[0]));
+ for (i = 1; i < mc->nchan; i++)
+ DPRINTF((",%04x", mc->wValue[i]));
+#if defined(__FreeBSD__)
+ DPRINTF((" wIndex=%04x type=%d ctl='%d' "
+ "min=%d max=%d\n",
+ mc->wIndex, mc->type, mc->ctl,
+ mc->minval, mc->maxval));
+#else
+ DPRINTF((" wIndex=%04x type=%d name='%s' unit='%s' "
+ "min=%d max=%d\n",
+ mc->wIndex, mc->type, mc->ctlname, mc->ctlunit,
+ mc->minval, mc->maxval));
+#endif
+ }
+#endif
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static char *
+uaudio_id_name(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ static char buf[32];
+
+ snprintf(buf, sizeof(buf), "i%d", id);
+ return buf;
+}
+#endif
+
+#ifdef USB_DEBUG
+static void
+uaudio_dump_cluster(const struct usb_audio_cluster *cl)
+{
+ static const char *channel_names[16] = {
+ "LEFT", "RIGHT", "CENTER", "LFE",
+ "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER",
+ "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP",
+ "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15",
+ };
+ int cc, i, first;
+
+ cc = UGETW(cl->wChannelConfig);
+ printf("cluster: bNrChannels=%u wChannelConfig=0x%.4x",
+ cl->bNrChannels, cc);
+ first = TRUE;
+ for (i = 0; cc != 0; i++) {
+ if (cc & 1) {
+ printf("%c%s", first ? '<' : ',', channel_names[i]);
+ first = FALSE;
+ }
+ cc = cc >> 1;
+ }
+ printf("> iChannelNames=%u", cl->iChannelNames);
+}
+#endif
+
+static struct usb_audio_cluster
+uaudio_get_cluster(int id, const struct io_terminal *iot)
+{
+ struct usb_audio_cluster r;
+ const usb_descriptor_t *dp;
+ int i;
+
+ for (i = 0; i < 25; i++) { /* avoid infinite loops */
+ dp = iot[id].d.desc;
+ if (dp == 0)
+ goto bad;
+ switch (dp->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ r.bNrChannels = iot[id].d.it->bNrChannels;
+ USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig));
+ r.iChannelNames = iot[id].d.it->iChannelNames;
+ return r;
+ case UDESCSUB_AC_OUTPUT:
+ id = iot[id].d.ot->bSourceId;
+ break;
+ case UDESCSUB_AC_MIXER:
+ r = *(const struct usb_audio_cluster *)
+ &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
+ return r;
+ case UDESCSUB_AC_SELECTOR:
+ /* XXX This is not really right */
+ id = iot[id].d.su->baSourceId[0];
+ break;
+ case UDESCSUB_AC_FEATURE:
+ id = iot[id].d.fu->bSourceId;
+ break;
+ case UDESCSUB_AC_PROCESSING:
+ r = *(const struct usb_audio_cluster *)
+ &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
+ return r;
+ case UDESCSUB_AC_EXTENSION:
+ r = *(const struct usb_audio_cluster *)
+ &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
+ return r;
+ default:
+ goto bad;
+ }
+ }
+ bad:
+ printf("uaudio_get_cluster: bad data\n");
+ memset(&r, 0, sizeof r);
+ return r;
+
+}
+
+static void
+uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+#ifdef USB_DEBUG
+ const struct usb_audio_input_terminal *d = iot[id].d.it;
+
+ DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x "
+ "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
+ "iChannelNames=%d iTerminal=%d\n",
+ d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
+ d->bNrChannels, UGETW(d->wChannelConfig),
+ d->iChannelNames, d->iTerminal));
+#endif
+}
+
+static void
+uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+#ifdef USB_DEBUG
+ const struct usb_audio_output_terminal *d;
+
+ d = iot[id].d.ot;
+ DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x "
+ "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
+ d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
+ d->bSourceId, d->iTerminal));
+#endif
+}
+
+static void
+uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_mixer_unit *d = iot[id].d.mu;
+ const struct usb_audio_mixer_unit_1 *d1;
+ int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k;
+ const uByte *bm;
+ struct mixerctl mix;
+
+ DPRINTFN(2,("uaudio_add_mixer: bUnitId=%d bNrInPins=%d\n",
+ d->bUnitId, d->bNrInPins));
+
+ /* Compute the number of input channels */
+ ichs = 0;
+ for (i = 0; i < d->bNrInPins; i++)
+ ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
+
+ /* and the number of output channels */
+ d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
+ ochs = d1->bNrChannels;
+ DPRINTFN(2,("uaudio_add_mixer: ichs=%d ochs=%d\n", ichs, ochs));
+
+ bm = d1->bmControls;
+ mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+ uaudio_determine_class(&iot[id], &mix);
+ mix.type = MIX_SIGNED_16;
+#if !defined(__FreeBSD__) /* XXXXX */
+ mix.ctlunit = AudioNvolume;
+#endif
+
+#define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
+ for (p = i = 0; i < d->bNrInPins; i++) {
+ chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
+ mc = 0;
+ for (c = 0; c < chs; c++) {
+ mo = 0;
+ for (o = 0; o < ochs; o++) {
+ bno = (p + c) * ochs + o;
+ if (BIT(bno))
+ mo++;
+ }
+ if (mo == 1)
+ mc++;
+ }
+ if (mc == chs && chs <= MIX_MAX_CHAN) {
+ k = 0;
+ for (c = 0; c < chs; c++)
+ for (o = 0; o < ochs; o++) {
+ bno = (p + c) * ochs + o;
+ if (BIT(bno))
+ mix.wValue[k++] =
+ MAKE(p+c+1, o+1);
+ }
+#if !defined(__FreeBSD__)
+ snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s",
+ d->bUnitId, uaudio_id_name(sc, iot,
+ d->baSourceId[i]));
+#endif
+ mix.nchan = chs;
+ uaudio_mixer_add_ctl(sc, &mix);
+ } else {
+ /* XXX */
+ }
+#undef BIT
+ p += chs;
+ }
+
+}
+
+static void
+uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_selector_unit *d;
+ struct mixerctl mix;
+#if !defined(__FreeBSD__)
+ int i, wp;
+#else
+ int i;
+ struct mixerctl dummy;
+#endif
+
+ d = iot[id].d.su;
+ DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n",
+ d->bUnitId, d->bNrInPins));
+ mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+ mix.wValue[0] = MAKE(0, 0);
+ uaudio_determine_class(&iot[id], &mix);
+ mix.nchan = 1;
+ mix.type = MIX_SELECTOR;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+ mix.minval = 1;
+ mix.maxval = d->bNrInPins;
+ mix.mul = mix.maxval - mix.minval;
+ for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
+ mix.slctrtype[i] = SOUND_MIXER_NRDEVICES;
+ }
+ for (i = mix.minval; i <= mix.maxval; i++) {
+ mix.slctrtype[i - 1] = uaudio_feature_name(&iot[d->baSourceId[i - 1]], &dummy);
+ }
+#else
+ mix.ctlunit = "";
+ mix.minval = 1;
+ mix.maxval = d->bNrInPins;
+ mix.mul = mix.maxval - mix.minval;
+ wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
+ for (i = 1; i <= d->bNrInPins; i++) {
+ wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp,
+ "i%d", d->baSourceId[i - 1]);
+ if (wp > MAX_AUDIO_DEV_LEN - 1)
+ break;
+ }
+#endif
+ uaudio_mixer_add_ctl(sc, &mix);
+}
+
+#ifdef USB_DEBUG
+static const char *
+uaudio_get_terminal_name(int terminal_type)
+{
+ static char buf[100];
+
+ switch (terminal_type) {
+ /* USB terminal types */
+ case UAT_UNDEFINED: return "UAT_UNDEFINED";
+ case UAT_STREAM: return "UAT_STREAM";
+ case UAT_VENDOR: return "UAT_VENDOR";
+ /* input terminal types */
+ case UATI_UNDEFINED: return "UATI_UNDEFINED";
+ case UATI_MICROPHONE: return "UATI_MICROPHONE";
+ case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE";
+ case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE";
+ case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE";
+ case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY";
+ case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR";
+ /* output terminal types */
+ case UATO_UNDEFINED: return "UATO_UNDEFINED";
+ case UATO_SPEAKER: return "UATO_SPEAKER";
+ case UATO_HEADPHONES: return "UATO_HEADPHONES";
+ case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO";
+ case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER";
+ case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER";
+ case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER";
+ case UATO_SUBWOOFER: return "UATO_SUBWOOFER";
+ /* bidir terminal types */
+ case UATB_UNDEFINED: return "UATB_UNDEFINED";
+ case UATB_HANDSET: return "UATB_HANDSET";
+ case UATB_HEADSET: return "UATB_HEADSET";
+ case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE";
+ case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP";
+ case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC";
+ /* telephony terminal types */
+ case UATT_UNDEFINED: return "UATT_UNDEFINED";
+ case UATT_PHONELINE: return "UATT_PHONELINE";
+ case UATT_TELEPHONE: return "UATT_TELEPHONE";
+ case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE";
+ /* external terminal types */
+ case UATE_UNDEFINED: return "UATE_UNDEFINED";
+ case UATE_ANALOGCONN: return "UATE_ANALOGCONN";
+ case UATE_LINECONN: return "UATE_LINECONN";
+ case UATE_LEGACYCONN: return "UATE_LEGACYCONN";
+ case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC";
+ case UATE_SPDIF: return "UATE_SPDIF";
+ case UATE_1394DA: return "UATE_1394DA";
+ case UATE_1394DV: return "UATE_1394DV";
+ /* embedded function terminal types */
+ case UATF_UNDEFINED: return "UATF_UNDEFINED";
+ case UATF_CALIBNOISE: return "UATF_CALIBNOISE";
+ case UATF_EQUNOISE: return "UATF_EQUNOISE";
+ case UATF_CDPLAYER: return "UATF_CDPLAYER";
+ case UATF_DAT: return "UATF_DAT";
+ case UATF_DCC: return "UATF_DCC";
+ case UATF_MINIDISK: return "UATF_MINIDISK";
+ case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE";
+ case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH";
+ case UATF_VCRAUDIO: return "UATF_VCRAUDIO";
+ case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO";
+ case UATF_DVDAUDIO: return "UATF_DVDAUDIO";
+ case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO";
+ case UATF_SATELLITE: return "UATF_SATELLITE";
+ case UATF_CABLETUNER: return "UATF_CABLETUNER";
+ case UATF_DSS: return "UATF_DSS";
+ case UATF_RADIORECV: return "UATF_RADIORECV";
+ case UATF_RADIOXMIT: return "UATF_RADIOXMIT";
+ case UATF_MULTITRACK: return "UATF_MULTITRACK";
+ case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER";
+ default:
+ snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type);
+ return buf;
+ }
+}
+#endif
+
+static int
+uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix)
+{
+ int terminal_type;
+
+ if (iot == NULL || iot->output == NULL) {
+ mix->class = UAC_OUTPUT;
+ return 0;
+ }
+ terminal_type = 0;
+ if (iot->output->size == 1)
+ terminal_type = iot->output->terminals[0];
+ /*
+ * If the only output terminal is USB,
+ * the class is UAC_RECORD.
+ */
+ if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
+ mix->class = UAC_RECORD;
+ if (iot->inputs_size == 1
+ && iot->inputs[0] != NULL
+ && iot->inputs[0]->size == 1)
+ return iot->inputs[0]->terminals[0];
+ else
+ return 0;
+ }
+ /*
+ * If the ultimate destination of the unit is just one output
+ * terminal and the unit is connected to the output terminal
+ * directly, the class is UAC_OUTPUT.
+ */
+ if (terminal_type != 0 && iot->direct) {
+ mix->class = UAC_OUTPUT;
+ return terminal_type;
+ }
+ /*
+ * If the unit is connected to just one input terminal,
+ * the class is UAC_INPUT.
+ */
+ if (iot->inputs_size == 1 && iot->inputs[0] != NULL
+ && iot->inputs[0]->size == 1) {
+ mix->class = UAC_INPUT;
+ return iot->inputs[0]->terminals[0];
+ }
+ /*
+ * Otherwise, the class is UAC_OUTPUT.
+ */
+ mix->class = UAC_OUTPUT;
+ return terminal_type;
+}
+
+#if defined(__FreeBSD__)
+static int
+uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
+{
+ int terminal_type;
+
+ terminal_type = uaudio_determine_class(iot, mix);
+ if (mix->class == UAC_RECORD && terminal_type == 0)
+ return SOUND_MIXER_IMIX;
+ DPRINTF(("%s: terminal_type=%s\n", __func__,
+ uaudio_get_terminal_name(terminal_type)));
+ switch (terminal_type) {
+ case UAT_STREAM:
+ return SOUND_MIXER_PCM;
+
+ case UATI_MICROPHONE:
+ case UATI_DESKMICROPHONE:
+ case UATI_PERSONALMICROPHONE:
+ case UATI_OMNIMICROPHONE:
+ case UATI_MICROPHONEARRAY:
+ case UATI_PROCMICROPHONEARR:
+ return SOUND_MIXER_MIC;
+
+ case UATO_SPEAKER:
+ case UATO_DESKTOPSPEAKER:
+ case UATO_ROOMSPEAKER:
+ case UATO_COMMSPEAKER:
+ return SOUND_MIXER_SPEAKER;
+
+ case UATE_ANALOGCONN:
+ case UATE_LINECONN:
+ case UATE_LEGACYCONN:
+ return SOUND_MIXER_LINE;
+
+ case UATE_DIGITALAUIFC:
+ case UATE_SPDIF:
+ case UATE_1394DA:
+ case UATE_1394DV:
+ return SOUND_MIXER_ALTPCM;
+
+ case UATF_CDPLAYER:
+ return SOUND_MIXER_CD;
+
+ case UATF_SYNTHESIZER:
+ return SOUND_MIXER_SYNTH;
+
+ case UATF_VIDEODISCAUDIO:
+ case UATF_DVDAUDIO:
+ case UATF_TVTUNERAUDIO:
+ return SOUND_MIXER_VIDEO;
+
+/* telephony terminal types */
+ case UATT_UNDEFINED:
+ case UATT_PHONELINE:
+ case UATT_TELEPHONE:
+ case UATT_DOWNLINEPHONE:
+ return SOUND_MIXER_PHONEIN;
+/* return SOUND_MIXER_PHONEOUT;*/
+
+ case UATF_RADIORECV:
+ case UATF_RADIOXMIT:
+ return SOUND_MIXER_RADIO;
+
+ case UAT_UNDEFINED:
+ case UAT_VENDOR:
+ case UATI_UNDEFINED:
+/* output terminal types */
+ case UATO_UNDEFINED:
+ case UATO_DISPLAYAUDIO:
+ case UATO_SUBWOOFER:
+ case UATO_HEADPHONES:
+/* bidir terminal types */
+ case UATB_UNDEFINED:
+ case UATB_HANDSET:
+ case UATB_HEADSET:
+ case UATB_SPEAKERPHONE:
+ case UATB_SPEAKERPHONEESUP:
+ case UATB_SPEAKERPHONEECANC:
+/* external terminal types */
+ case UATE_UNDEFINED:
+/* embedded function terminal types */
+ case UATF_UNDEFINED:
+ case UATF_CALIBNOISE:
+ case UATF_EQUNOISE:
+ case UATF_DAT:
+ case UATF_DCC:
+ case UATF_MINIDISK:
+ case UATF_ANALOGTAPE:
+ case UATF_PHONOGRAPH:
+ case UATF_VCRAUDIO:
+ case UATF_SATELLITE:
+ case UATF_CABLETUNER:
+ case UATF_DSS:
+ case UATF_MULTITRACK:
+ case 0xffff:
+ default:
+ DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type));
+ return SOUND_MIXER_VOLUME;
+ }
+ return SOUND_MIXER_VOLUME;
+}
+#else
+static const char *
+uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
+{
+ int terminal_type;
+
+ terminal_type = uaudio_determine_class(iot, mix);
+ if (mix->class == UAC_RECORD && terminal_type == 0)
+ return AudioNmixerout;
+ DPRINTF(("%s: terminal_type=%s\n", __func__,
+ uaudio_get_terminal_name(terminal_type)));
+ switch (terminal_type) {
+ case UAT_STREAM:
+ return AudioNdac;
+
+ case UATI_MICROPHONE:
+ case UATI_DESKMICROPHONE:
+ case UATI_PERSONALMICROPHONE:
+ case UATI_OMNIMICROPHONE:
+ case UATI_MICROPHONEARRAY:
+ case UATI_PROCMICROPHONEARR:
+ return AudioNmicrophone;
+
+ case UATO_SPEAKER:
+ case UATO_DESKTOPSPEAKER:
+ case UATO_ROOMSPEAKER:
+ case UATO_COMMSPEAKER:
+ return AudioNspeaker;
+
+ case UATO_HEADPHONES:
+ return AudioNheadphone;
+
+ case UATO_SUBWOOFER:
+ return AudioNlfe;
+
+ /* telephony terminal types */
+ case UATT_UNDEFINED:
+ case UATT_PHONELINE:
+ case UATT_TELEPHONE:
+ case UATT_DOWNLINEPHONE:
+ return "phone";
+
+ case UATE_ANALOGCONN:
+ case UATE_LINECONN:
+ case UATE_LEGACYCONN:
+ return AudioNline;
+
+ case UATE_DIGITALAUIFC:
+ case UATE_SPDIF:
+ case UATE_1394DA:
+ case UATE_1394DV:
+ return AudioNaux;
+
+ case UATF_CDPLAYER:
+ return AudioNcd;
+
+ case UATF_SYNTHESIZER:
+ return AudioNfmsynth;
+
+ case UATF_VIDEODISCAUDIO:
+ case UATF_DVDAUDIO:
+ case UATF_TVTUNERAUDIO:
+ return AudioNvideo;
+
+ case UAT_UNDEFINED:
+ case UAT_VENDOR:
+ case UATI_UNDEFINED:
+/* output terminal types */
+ case UATO_UNDEFINED:
+ case UATO_DISPLAYAUDIO:
+/* bidir terminal types */
+ case UATB_UNDEFINED:
+ case UATB_HANDSET:
+ case UATB_HEADSET:
+ case UATB_SPEAKERPHONE:
+ case UATB_SPEAKERPHONEESUP:
+ case UATB_SPEAKERPHONEECANC:
+/* external terminal types */
+ case UATE_UNDEFINED:
+/* embedded function terminal types */
+ case UATF_UNDEFINED:
+ case UATF_CALIBNOISE:
+ case UATF_EQUNOISE:
+ case UATF_DAT:
+ case UATF_DCC:
+ case UATF_MINIDISK:
+ case UATF_ANALOGTAPE:
+ case UATF_PHONOGRAPH:
+ case UATF_VCRAUDIO:
+ case UATF_SATELLITE:
+ case UATF_CABLETUNER:
+ case UATF_DSS:
+ case UATF_RADIORECV:
+ case UATF_RADIOXMIT:
+ case UATF_MULTITRACK:
+ case 0xffff:
+ default:
+ DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type));
+ return AudioNmaster;
+ }
+ return AudioNmaster;
+}
+#endif
+
+static void
+uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_feature_unit *d;
+ const uByte *ctls;
+ int ctlsize;
+ int nchan;
+ u_int fumask, mmask, cmask;
+ struct mixerctl mix;
+ int chan, ctl, i, unit;
+#if defined(__FreeBSD__)
+ int mixernumber;
+#else
+ const char *mixername;
+#endif
+
+#define GET(i) (ctls[(i)*ctlsize] | \
+ (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
+ d = iot[id].d.fu;
+ ctls = d->bmaControls;
+ ctlsize = d->bControlSize;
+ nchan = (d->bLength - 7) / ctlsize;
+ mmask = GET(0);
+ /* Figure out what we can control */
+ for (cmask = 0, chan = 1; chan < nchan; chan++) {
+ DPRINTFN(9,("uaudio_add_feature: chan=%d mask=%x\n",
+ chan, GET(chan)));
+ cmask |= GET(chan);
+ }
+
+#if !defined(__FreeBSD__)
+ DPRINTFN(1,("uaudio_add_feature: bUnitId=%d, "
+ "%d channels, mmask=0x%04x, cmask=0x%04x\n",
+ d->bUnitId, nchan, mmask, cmask));
+#endif
+
+ if (nchan > MIX_MAX_CHAN)
+ nchan = MIX_MAX_CHAN;
+ unit = d->bUnitId;
+ mix.wIndex = MAKE(unit, sc->sc_ac_iface);
+ for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) {
+ fumask = FU_MASK(ctl);
+ DPRINTFN(4,("uaudio_add_feature: ctl=%d fumask=0x%04x\n",
+ ctl, fumask));
+ if (mmask & fumask) {
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE(ctl, 0);
+ } else if (cmask & fumask) {
+ mix.nchan = nchan - 1;
+ for (i = 1; i < nchan; i++) {
+ if (GET(i) & fumask)
+ mix.wValue[i-1] = MAKE(ctl, i);
+ else
+ mix.wValue[i-1] = -1;
+ }
+ } else {
+ continue;
+ }
+#undef GET
+
+#if defined(__FreeBSD__)
+ mixernumber = uaudio_feature_name(&iot[id], &mix);
+#else
+ mixername = uaudio_feature_name(&iot[id], &mix);
+#endif
+ switch (ctl) {
+ case MUTE_CONTROL:
+ mix.type = MIX_ON_OFF;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES;
+#else
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNmute);
+#endif
+ break;
+ case VOLUME_CONTROL:
+ mix.type = MIX_SIGNED_16;
+#if defined(__FreeBSD__)
+ mix.ctl = mixernumber;
+#else
+ mix.ctlunit = AudioNvolume;
+ strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
+#endif
+ break;
+ case BASS_CONTROL:
+ mix.type = MIX_SIGNED_8;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_BASS;
+#else
+ mix.ctlunit = AudioNbass;
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNbass);
+#endif
+ break;
+ case MID_CONTROL:
+ mix.type = MIX_SIGNED_8;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+#else
+ mix.ctlunit = AudioNmid;
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNmid);
+#endif
+ break;
+ case TREBLE_CONTROL:
+ mix.type = MIX_SIGNED_8;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_TREBLE;
+#else
+ mix.ctlunit = AudioNtreble;
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNtreble);
+#endif
+ break;
+ case GRAPHIC_EQUALIZER_CONTROL:
+ continue; /* XXX don't add anything */
+ break;
+ case AGC_CONTROL:
+ mix.type = MIX_ON_OFF;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+#else
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
+ mixername, AudioNagc);
+#endif
+ break;
+ case DELAY_CONTROL:
+ mix.type = MIX_UNSIGNED_16;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+#else
+ mix.ctlunit = "4 ms";
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNdelay);
+#endif
+ break;
+ case BASS_BOOST_CONTROL:
+ mix.type = MIX_ON_OFF;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+#else
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNbassboost);
+#endif
+ break;
+ case LOUDNESS_CONTROL:
+ mix.type = MIX_ON_OFF;
+#if defined(__FreeBSD__)
+ mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */
+#else
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname),
+ "%s.%s", mixername, AudioNloudness);
+#endif
+ break;
+ }
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+}
+
+static void
+uaudio_add_processing_updown(struct uaudio_softc *sc,
+ const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_processing_unit *d;
+ const struct usb_audio_processing_unit_1 *d1;
+ const struct usb_audio_processing_unit_updown *ud;
+ struct mixerctl mix;
+ int i;
+
+ d = iot[id].d.pu;
+ d1 = (const struct usb_audio_processing_unit_1 *)
+ &d->baSourceId[d->bNrInPins];
+ ud = (const struct usb_audio_processing_unit_updown *)
+ &d1->bmControls[d1->bControlSize];
+ DPRINTFN(2,("uaudio_add_processing_updown: bUnitId=%d bNrModes=%d\n",
+ d->bUnitId, ud->bNrModes));
+
+ if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
+ DPRINTF(("uaudio_add_processing_updown: no mode select\n"));
+ return;
+ }
+
+ mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0);
+ uaudio_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF; /* XXX */
+#if !defined(__FreeBSD__)
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId);
+#endif
+
+ for (i = 0; i < ud->bNrModes; i++) {
+ DPRINTFN(2,("uaudio_add_processing_updown: i=%d bm=0x%x\n",
+ i, UGETW(ud->waModes[i])));
+ /* XXX */
+ }
+ uaudio_mixer_add_ctl(sc, &mix);
+}
+
+static void
+uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_processing_unit *d;
+ const struct usb_audio_processing_unit_1 *d1;
+ int ptype;
+ struct mixerctl mix;
+
+ d = iot[id].d.pu;
+ d1 = (const struct usb_audio_processing_unit_1 *)
+ &d->baSourceId[d->bNrInPins];
+ ptype = UGETW(d->wProcessType);
+ DPRINTFN(2,("uaudio_add_processing: wProcessType=%d bUnitId=%d "
+ "bNrInPins=%d\n", ptype, d->bUnitId, d->bNrInPins));
+
+ if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
+ mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0);
+ uaudio_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF;
+#if !defined(__FreeBSD__)
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable",
+ d->bUnitId, ptype);
+#endif
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+
+ switch(ptype) {
+ case UPDOWNMIX_PROCESS:
+ uaudio_add_processing_updown(sc, iot, id);
+ break;
+ case DOLBY_PROLOGIC_PROCESS:
+ case P3D_STEREO_EXTENDER_PROCESS:
+ case REVERBATION_PROCESS:
+ case CHORUS_PROCESS:
+ case DYN_RANGE_COMP_PROCESS:
+ default:
+#ifdef USB_DEBUG
+ printf("uaudio_add_processing: unit %d, type=%d not impl.\n",
+ d->bUnitId, ptype);
+#endif
+ break;
+ }
+}
+
+static void
+uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+ const struct usb_audio_extension_unit *d;
+ const struct usb_audio_extension_unit_1 *d1;
+ struct mixerctl mix;
+
+ d = iot[id].d.eu;
+ d1 = (const struct usb_audio_extension_unit_1 *)
+ &d->baSourceId[d->bNrInPins];
+ DPRINTFN(2,("uaudio_add_extension: bUnitId=%d bNrInPins=%d\n",
+ d->bUnitId, d->bNrInPins));
+
+ if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_XU)
+ return;
+
+ if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
+ mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0);
+ uaudio_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF;
+#if !defined(__FreeBSD__)
+ mix.ctlunit = "";
+ snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable",
+ d->bUnitId);
+#endif
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+}
+
+static struct terminal_list*
+uaudio_merge_terminal_list(const struct io_terminal *iot)
+{
+ struct terminal_list *tml;
+ uint16_t *ptm;
+ int i, len;
+
+ len = 0;
+ if (iot->inputs == NULL)
+ return NULL;
+ for (i = 0; i < iot->inputs_size; i++) {
+ if (iot->inputs[i] != NULL)
+ len += iot->inputs[i]->size;
+ }
+ tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT);
+ if (tml == NULL) {
+ printf("uaudio_merge_terminal_list: no memory\n");
+ return NULL;
+ }
+ tml->size = 0;
+ ptm = tml->terminals;
+ for (i = 0; i < iot->inputs_size; i++) {
+ if (iot->inputs[i] == NULL)
+ continue;
+ if (iot->inputs[i]->size > len)
+ break;
+ memcpy(ptm, iot->inputs[i]->terminals,
+ iot->inputs[i]->size * sizeof(uint16_t));
+ tml->size += iot->inputs[i]->size;
+ ptm += iot->inputs[i]->size;
+ len -= iot->inputs[i]->size;
+ }
+ return tml;
+}
+
+static struct terminal_list *
+uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id)
+{
+ struct terminal_list *tml;
+ struct io_terminal *it;
+ int src_id, i;
+
+ it = &iot[id];
+ if (it->output != NULL) {
+ /* already has outtype? */
+ for (i = 0; i < it->output->size; i++)
+ if (it->output->terminals[i] == outtype)
+ return uaudio_merge_terminal_list(it);
+ tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1),
+ M_TEMP, M_NOWAIT);
+ if (tml == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return uaudio_merge_terminal_list(it);
+ }
+ memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size));
+ tml->terminals[it->output->size] = outtype;
+ tml->size++;
+ free(it->output, M_TEMP);
+ it->output = tml;
+ if (it->inputs != NULL) {
+ for (i = 0; i < it->inputs_size; i++)
+ if (it->inputs[i] != NULL)
+ free(it->inputs[i], M_TEMP);
+ free(it->inputs, M_TEMP);
+ }
+ it->inputs_size = 0;
+ it->inputs = NULL;
+ } else { /* end `iot[id] != NULL' */
+ it->inputs_size = 0;
+ it->inputs = NULL;
+ it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
+ if (it->output == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ it->output->terminals[0] = outtype;
+ it->output->size = 1;
+ it->direct = FALSE;
+ }
+
+ switch (it->d.desc->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
+ if (tml == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ free(it->inputs, M_TEMP);
+ it->inputs = NULL;
+ return NULL;
+ }
+ it->inputs[0] = tml;
+ tml->terminals[0] = UGETW(it->d.it->wTerminalType);
+ tml->size = 1;
+ it->inputs_size = 1;
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_FEATURE:
+ src_id = it->d.fu->bSourceId;
+ it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return uaudio_io_terminaltype(outtype, iot, src_id);
+ }
+ it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
+ it->inputs_size = 1;
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_OUTPUT:
+ it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ src_id = it->d.ot->bSourceId;
+ it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
+ it->inputs_size = 1;
+ iot[src_id].direct = TRUE;
+ return NULL;
+ case UDESCSUB_AC_MIXER:
+ it->inputs_size = 0;
+ it->inputs = malloc(sizeof(struct terminal_list *)
+ * it->d.mu->bNrInPins, M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ for (i = 0; i < it->d.mu->bNrInPins; i++) {
+ src_id = it->d.mu->baSourceId[i];
+ it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+ src_id);
+ it->inputs_size++;
+ }
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_SELECTOR:
+ it->inputs_size = 0;
+ it->inputs = malloc(sizeof(struct terminal_list *)
+ * it->d.su->bNrInPins, M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ for (i = 0; i < it->d.su->bNrInPins; i++) {
+ src_id = it->d.su->baSourceId[i];
+ it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+ src_id);
+ it->inputs_size++;
+ }
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_PROCESSING:
+ it->inputs_size = 0;
+ it->inputs = malloc(sizeof(struct terminal_list *)
+ * it->d.pu->bNrInPins, M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ for (i = 0; i < it->d.pu->bNrInPins; i++) {
+ src_id = it->d.pu->baSourceId[i];
+ it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+ src_id);
+ it->inputs_size++;
+ }
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_EXTENSION:
+ it->inputs_size = 0;
+ it->inputs = malloc(sizeof(struct terminal_list *)
+ * it->d.eu->bNrInPins, M_TEMP, M_NOWAIT);
+ if (it->inputs == NULL) {
+ printf("uaudio_io_terminaltype: no memory\n");
+ return NULL;
+ }
+ for (i = 0; i < it->d.eu->bNrInPins; i++) {
+ src_id = it->d.eu->baSourceId[i];
+ it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+ src_id);
+ it->inputs_size++;
+ }
+ return uaudio_merge_terminal_list(it);
+ case UDESCSUB_AC_HEADER:
+ default:
+ return NULL;
+ }
+}
+
+static usbd_status
+uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
+{
+ usbd_status err;
+
+ err = uaudio_identify_ac(sc, cdesc);
+ if (err)
+ return err;
+ return uaudio_identify_as(sc, cdesc);
+}
+
+static void
+uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai)
+{
+ size_t len;
+ struct as_info *nai;
+
+ len = sizeof(*ai) * (sc->sc_nalts + 1);
+ nai = malloc(len, M_USBDEV, M_NOWAIT);
+ if (nai == NULL) {
+ printf("uaudio_add_alt: no memory\n");
+ return;
+ }
+ /* Copy old data, if there was any */
+ if (sc->sc_nalts != 0) {
+ memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts));
+ free(sc->sc_alts, M_USBDEV);
+ }
+ sc->sc_alts = nai;
+ DPRINTFN(2,("uaudio_add_alt: adding alt=%d, enc=%d\n",
+ ai->alt, ai->encoding));
+ sc->sc_alts[sc->sc_nalts++] = *ai;
+}
+
+static usbd_status
+uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
+ int size, const usb_interface_descriptor_t *id)
+#define offs (*offsp)
+{
+ const struct usb_audio_streaming_interface_descriptor *asid;
+ const struct usb_audio_streaming_type1_descriptor *asf1d;
+ const usb_endpoint_descriptor_audio_t *ed;
+ const usb_endpoint_descriptor_audio_t *epdesc1;
+ const struct usb_audio_streaming_endpoint_descriptor *sed;
+ int format, chan, prec, enc;
+ int dir, type, sync;
+ struct as_info ai;
+ const char *format_str;
+
+ asid = (const void *)(buf + offs);
+
+ if (asid->bDescriptorType != UDESC_CS_INTERFACE ||
+ asid->bDescriptorSubtype != AS_GENERAL)
+ return USBD_INVAL;
+ DPRINTF(("uaudio_process_as: asid: bTerminakLink=%d wFormatTag=%d\n",
+ asid->bTerminalLink, UGETW(asid->wFormatTag)));
+ offs += asid->bLength;
+ if (offs > size)
+ return USBD_INVAL;
+
+ asf1d = (const void *)(buf + offs);
+ if (asf1d->bDescriptorType != UDESC_CS_INTERFACE ||
+ asf1d->bDescriptorSubtype != FORMAT_TYPE)
+ return USBD_INVAL;
+ offs += asf1d->bLength;
+ if (offs > size)
+ return USBD_INVAL;
+
+ if (asf1d->bFormatType != FORMAT_TYPE_I) {
+ printf("%s: ignored setting with type %d format\n",
+ device_get_nameunit(sc->sc_dev), UGETW(asid->wFormatTag));
+ return USBD_NORMAL_COMPLETION;
+ }
+
+ ed = (const void *)(buf + offs);
+ if (ed->bDescriptorType != UDESC_ENDPOINT)
+ return USBD_INVAL;
+ DPRINTF(("uaudio_process_as: endpoint[0] bLength=%d bDescriptorType=%d "
+ "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d "
+ "bInterval=%d bRefresh=%d bSynchAddress=%d\n",
+ ed->bLength, ed->bDescriptorType, ed->bEndpointAddress,
+ ed->bmAttributes, UGETW(ed->wMaxPacketSize),
+ ed->bInterval, ed->bRefresh, ed->bSynchAddress));
+ offs += ed->bLength;
+ if (offs > size)
+ return USBD_INVAL;
+ if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+ return USBD_INVAL;
+
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ type = UE_GET_ISO_TYPE(ed->bmAttributes);
+ if ((usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_INP_ASYNC) &&
+ dir == UE_DIR_IN && type == UE_ISO_ADAPT)
+ type = UE_ISO_ASYNC;
+
+ /* We can't handle endpoints that need a sync pipe yet. */
+ sync = FALSE;
+ if (dir == UE_DIR_IN && type == UE_ISO_ADAPT) {
+ sync = TRUE;
+#ifndef UAUDIO_MULTIPLE_ENDPOINTS
+ printf("%s: ignored input endpoint of type adaptive\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_NORMAL_COMPLETION;
+#endif
+ }
+ if (dir != UE_DIR_IN && type == UE_ISO_ASYNC) {
+ sync = TRUE;
+#ifndef UAUDIO_MULTIPLE_ENDPOINTS
+ printf("%s: ignored output endpoint of type async\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_NORMAL_COMPLETION;
+#endif
+ }
+
+ sed = (const void *)(buf + offs);
+ if (sed->bDescriptorType != UDESC_CS_ENDPOINT ||
+ sed->bDescriptorSubtype != AS_GENERAL)
+ return USBD_INVAL;
+ DPRINTF((" streadming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength));
+ offs += sed->bLength;
+ if (offs > size)
+ return USBD_INVAL;
+
+#ifdef UAUDIO_MULTIPLE_ENDPOINTS
+ if (sync && id->bNumEndpoints <= 1) {
+ printf("%s: a sync-pipe endpoint but no other endpoint\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_INVAL;
+ }
+#endif
+ if (!sync && id->bNumEndpoints > 1) {
+ printf("%s: non sync-pipe endpoint but multiple endpoints\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_INVAL;
+ }
+ epdesc1 = NULL;
+ if (id->bNumEndpoints > 1) {
+ epdesc1 = (const void*)(buf + offs);
+ if (epdesc1->bDescriptorType != UDESC_ENDPOINT)
+ return USBD_INVAL;
+ DPRINTF(("uaudio_process_as: endpoint[1] bLength=%d "
+ "bDescriptorType=%d bEndpointAddress=%d "
+ "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d "
+ "bRefresh=%d bSynchAddress=%d\n",
+ epdesc1->bLength, epdesc1->bDescriptorType,
+ epdesc1->bEndpointAddress, epdesc1->bmAttributes,
+ UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval,
+ epdesc1->bRefresh, epdesc1->bSynchAddress));
+ offs += epdesc1->bLength;
+ if (offs > size)
+ return USBD_INVAL;
+ if (epdesc1->bSynchAddress != 0) {
+ printf("%s: invalid endpoint: bSynchAddress=0\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_INVAL;
+ }
+ if (UE_GET_XFERTYPE(epdesc1->bmAttributes) != UE_ISOCHRONOUS) {
+ printf("%s: invalid endpoint: bmAttributes=0x%x\n",
+ device_get_nameunit(sc->sc_dev), epdesc1->bmAttributes);
+ return USBD_INVAL;
+ }
+ if (epdesc1->bEndpointAddress != ed->bSynchAddress) {
+ printf("%s: invalid endpoint addresses: "
+ "ep[0]->bSynchAddress=0x%x "
+ "ep[1]->bEndpointAddress=0x%x\n",
+ device_get_nameunit(sc->sc_dev), ed->bSynchAddress,
+ epdesc1->bEndpointAddress);
+ return USBD_INVAL;
+ }
+ /* UE_GET_ADDR(epdesc1->bEndpointAddress), and epdesc1->bRefresh */
+ }
+
+ format = UGETW(asid->wFormatTag);
+ chan = asf1d->bNrChannels;
+ prec = asf1d->bBitResolution;
+ if (prec != 8 && prec != 16 && prec != 24 && prec != 32) {
+ printf("%s: ignored setting with precision %d\n",
+ device_get_nameunit(sc->sc_dev), prec);
+ return USBD_NORMAL_COMPLETION;
+ }
+ switch (format) {
+ case UA_FMT_PCM:
+ if (prec == 8) {
+ sc->sc_altflags |= HAS_8;
+ } else if (prec == 16) {
+ sc->sc_altflags |= HAS_16;
+ } else if (prec == 24) {
+ sc->sc_altflags |= HAS_24;
+ } else if (prec == 32) {
+ sc->sc_altflags |= HAS_32;
+ }
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ format_str = "pcm";
+ break;
+ case UA_FMT_PCM8:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ sc->sc_altflags |= HAS_8U;
+ format_str = "pcm8";
+ break;
+ case UA_FMT_ALAW:
+ enc = AUDIO_ENCODING_ALAW;
+ sc->sc_altflags |= HAS_ALAW;
+ format_str = "alaw";
+ break;
+ case UA_FMT_MULAW:
+ enc = AUDIO_ENCODING_ULAW;
+ sc->sc_altflags |= HAS_MULAW;
+ format_str = "mulaw";
+ break;
+ case UA_FMT_IEEE_FLOAT:
+ default:
+ printf("%s: ignored setting with format %d\n",
+ device_get_nameunit(sc->sc_dev), format);
+ return USBD_NORMAL_COMPLETION;
+ }
+#ifdef USB_DEBUG
+ printf("%s: %s: %dch, %d/%dbit, %s,", device_get_nameunit(sc->sc_dev),
+ dir == UE_DIR_IN ? "recording" : "playback",
+ chan, prec, asf1d->bSubFrameSize * 8, format_str);
+ if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+ printf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ } else {
+ int r;
+ printf(" %d", UA_GETSAMP(asf1d, 0));
+ for (r = 1; r < asf1d->bSamFreqType; r++)
+ printf(",%d", UA_GETSAMP(asf1d, r));
+ printf("Hz\n");
+ }
+#endif
+#if defined(__FreeBSD__)
+ if (sc->uaudio_sndstat_flag != 0) {
+ sbuf_printf(&(sc->uaudio_sndstat), "\n\t");
+ sbuf_printf(&(sc->uaudio_sndstat),
+ "mode %d:(%s) %dch, %d/%dbit, %s,",
+ id->bAlternateSetting,
+ dir == UE_DIR_IN ? "input" : "output",
+ chan, prec, asf1d->bSubFrameSize * 8, format_str);
+ if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+ sbuf_printf(&(sc->uaudio_sndstat), " %d-%dHz",
+ UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ } else {
+ int r;
+ sbuf_printf(&(sc->uaudio_sndstat),
+ " %d", UA_GETSAMP(asf1d, 0));
+ for (r = 1; r < asf1d->bSamFreqType; r++)
+ sbuf_printf(&(sc->uaudio_sndstat),
+ ",%d", UA_GETSAMP(asf1d, r));
+ sbuf_printf(&(sc->uaudio_sndstat), "Hz");
+ }
+ }
+#endif
+ ai.alt = id->bAlternateSetting;
+ ai.encoding = enc;
+ ai.attributes = sed->bmAttributes;
+ ai.idesc = id;
+ ai.edesc = ed;
+ ai.edesc1 = epdesc1;
+ ai.asf1desc = asf1d;
+ ai.sc_busy = 0;
+ ai.ifaceh = NULL;
+ uaudio_add_alt(sc, &ai);
+#ifdef USB_DEBUG
+ if (ai.attributes & UA_SED_FREQ_CONTROL)
+ DPRINTFN(1, ("uaudio_process_as: FREQ_CONTROL\n"));
+ if (ai.attributes & UA_SED_PITCH_CONTROL)
+ DPRINTFN(1, ("uaudio_process_as: PITCH_CONTROL\n"));
+#endif
+ sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD;
+
+ return USBD_NORMAL_COMPLETION;
+}
+#undef offs
+
+static usbd_status
+uaudio_identify_as(struct uaudio_softc *sc,
+ const usb_config_descriptor_t *cdesc)
+{
+ const usb_interface_descriptor_t *id;
+ const char *buf;
+ int size, offs;
+
+ size = UGETW(cdesc->wTotalLength);
+ buf = (const char *)cdesc;
+
+ /* Locate the AudioStreaming interface descriptor. */
+ offs = 0;
+ id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM);
+ if (id == NULL)
+ return USBD_INVAL;
+
+#if defined(__FreeBSD__)
+ sc->uaudio_sndstat_flag = 0;
+ if (sbuf_new(&(sc->uaudio_sndstat), NULL, 4096, SBUF_AUTOEXTEND) != NULL)
+ sc->uaudio_sndstat_flag = 1;
+#endif
+ /* Loop through all the alternate settings. */
+ while (offs <= size) {
+ DPRINTFN(2, ("uaudio_identify: interface=%d offset=%d\n",
+ id->bInterfaceNumber, offs));
+ switch (id->bNumEndpoints) {
+ case 0:
+ DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n",
+ id->bAlternateSetting));
+ sc->sc_nullalt = id->bAlternateSetting;
+ break;
+ case 1:
+#ifdef UAUDIO_MULTIPLE_ENDPOINTS
+ case 2:
+#endif
+ uaudio_process_as(sc, buf, &offs, size, id);
+ break;
+ default:
+ printf("%s: ignored audio interface with %d "
+ "endpoints\n",
+ device_get_nameunit(sc->sc_dev), id->bNumEndpoints);
+ break;
+ }
+ id = uaudio_find_iface(buf, size, &offs,UISUBCLASS_AUDIOSTREAM);
+ if (id == NULL)
+ break;
+ }
+#if defined(__FreeBSD__)
+ sbuf_finish(&(sc->uaudio_sndstat));
+#endif
+ if (offs > size)
+ return USBD_INVAL;
+ DPRINTF(("uaudio_identify_as: %d alts available\n", sc->sc_nalts));
+
+ if (sc->sc_mode == 0) {
+ printf("%s: no usable endpoint found\n",
+ device_get_nameunit(sc->sc_dev));
+ return USBD_INVAL;
+ }
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+static usbd_status
+uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
+{
+ struct io_terminal* iot;
+ const usb_interface_descriptor_t *id;
+ const struct usb_audio_control_descriptor *acdp;
+ const usb_descriptor_t *dp;
+ const struct usb_audio_output_terminal *pot;
+ struct terminal_list *tml;
+ const char *buf, *ibuf, *ibufend;
+ int size, offs, aclen, ndps, i, j;
+
+ size = UGETW(cdesc->wTotalLength);
+ buf = (const char *)cdesc;
+
+ /* Locate the AudioControl interface descriptor. */
+ offs = 0;
+ id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL);
+ if (id == NULL)
+ return USBD_INVAL;
+ if (offs + sizeof *acdp > size)
+ return USBD_INVAL;
+ sc->sc_ac_iface = id->bInterfaceNumber;
+ DPRINTFN(2,("uaudio_identify_ac: AC interface is %d\n", sc->sc_ac_iface));
+
+ /* A class-specific AC interface header should follow. */
+ ibuf = buf + offs;
+ acdp = (const struct usb_audio_control_descriptor *)ibuf;
+ if (acdp->bDescriptorType != UDESC_CS_INTERFACE ||
+ acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)
+ return USBD_INVAL;
+ aclen = UGETW(acdp->wTotalLength);
+ if (offs + aclen > size)
+ return USBD_INVAL;
+
+ if (!(usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BAD_ADC) &&
+ UGETW(acdp->bcdADC) != UAUDIO_VERSION)
+ return USBD_INVAL;
+
+ sc->sc_audio_rev = UGETW(acdp->bcdADC);
+ DPRINTFN(2,("uaudio_identify_ac: found AC header, vers=%03x, len=%d\n",
+ sc->sc_audio_rev, aclen));
+
+ sc->sc_nullalt = -1;
+
+ /* Scan through all the AC specific descriptors */
+ ibufend = ibuf + aclen;
+ dp = (const usb_descriptor_t *)ibuf;
+ ndps = 0;
+ iot = malloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO);
+ if (iot == NULL) {
+ printf("%s: no memory\n", __func__);
+ return USBD_NOMEM;
+ }
+ for (;;) {
+ ibuf += dp->bLength;
+ if (ibuf >= ibufend)
+ break;
+ dp = (const usb_descriptor_t *)ibuf;
+ if (ibuf + dp->bLength > ibufend) {
+ free(iot, M_TEMP);
+ return USBD_INVAL;
+ }
+ if (dp->bDescriptorType != UDESC_CS_INTERFACE) {
+ printf("uaudio_identify_ac: skip desc type=0x%02x\n",
+ dp->bDescriptorType);
+ continue;
+ }
+ i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId;
+ iot[i].d.desc = dp;
+ if (i > ndps)
+ ndps = i;
+ }
+ ndps++;
+
+ /* construct io_terminal */
+ for (i = 0; i < ndps; i++) {
+ dp = iot[i].d.desc;
+ if (dp == NULL)
+ continue;
+ if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT)
+ continue;
+ pot = iot[i].d.ot;
+ tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i);
+ if (tml != NULL)
+ free(tml, M_TEMP);
+ }
+
+#ifdef USB_DEBUG
+ for (i = 0; i < 256; i++) {
+ struct usb_audio_cluster cluster;
+
+ if (iot[i].d.desc == NULL)
+ continue;
+ printf("id %d:\t", i);
+ switch (iot[i].d.desc->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ printf("AC_INPUT type=%s\n", uaudio_get_terminal_name
+ (UGETW(iot[i].d.it->wTerminalType)));
+ printf("\t");
+ cluster = uaudio_get_cluster(i, iot);
+ uaudio_dump_cluster(&cluster);
+ printf("\n");
+ break;
+ case UDESCSUB_AC_OUTPUT:
+ printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name
+ (UGETW(iot[i].d.ot->wTerminalType)));
+ printf("src=%d\n", iot[i].d.ot->bSourceId);
+ break;
+ case UDESCSUB_AC_MIXER:
+ printf("AC_MIXER src=");
+ for (j = 0; j < iot[i].d.mu->bNrInPins; j++)
+ printf("%d ", iot[i].d.mu->baSourceId[j]);
+ printf("\n\t");
+ cluster = uaudio_get_cluster(i, iot);
+ uaudio_dump_cluster(&cluster);
+ printf("\n");
+ break;
+ case UDESCSUB_AC_SELECTOR:
+ printf("AC_SELECTOR src=");
+ for (j = 0; j < iot[i].d.su->bNrInPins; j++)
+ printf("%d ", iot[i].d.su->baSourceId[j]);
+ printf("\n");
+ break;
+ case UDESCSUB_AC_FEATURE:
+ printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
+ break;
+ case UDESCSUB_AC_PROCESSING:
+ printf("AC_PROCESSING src=");
+ for (j = 0; j < iot[i].d.pu->bNrInPins; j++)
+ printf("%d ", iot[i].d.pu->baSourceId[j]);
+ printf("\n\t");
+ cluster = uaudio_get_cluster(i, iot);
+ uaudio_dump_cluster(&cluster);
+ printf("\n");
+ break;
+ case UDESCSUB_AC_EXTENSION:
+ printf("AC_EXTENSION src=");
+ for (j = 0; j < iot[i].d.eu->bNrInPins; j++)
+ printf("%d ", iot[i].d.eu->baSourceId[j]);
+ printf("\n\t");
+ cluster = uaudio_get_cluster(i, iot);
+ uaudio_dump_cluster(&cluster);
+ printf("\n");
+ break;
+ default:
+ printf("unknown audio control (subtype=%d)\n",
+ iot[i].d.desc->bDescriptorSubtype);
+ }
+ for (j = 0; j < iot[i].inputs_size; j++) {
+ int k;
+ printf("\tinput%d: ", j);
+ tml = iot[i].inputs[j];
+ if (tml == NULL) {
+ printf("NULL\n");
+ continue;
+ }
+ for (k = 0; k < tml->size; k++)
+ printf("%s ", uaudio_get_terminal_name
+ (tml->terminals[k]));
+ printf("\n");
+ }
+ printf("\toutput: ");
+ tml = iot[i].output;
+ for (j = 0; j < tml->size; j++)
+ printf("%s ", uaudio_get_terminal_name(tml->terminals[j]));
+ printf("\n");
+ }
+#endif
+
+ for (i = 0; i < ndps; i++) {
+ dp = iot[i].d.desc;
+ if (dp == NULL)
+ continue;
+ DPRINTF(("uaudio_identify_ac: id=%d subtype=%d\n",
+ i, dp->bDescriptorSubtype));
+ switch (dp->bDescriptorSubtype) {
+ case UDESCSUB_AC_HEADER:
+ printf("uaudio_identify_ac: unexpected AC header\n");
+ break;
+ case UDESCSUB_AC_INPUT:
+ uaudio_add_input(sc, iot, i);
+ break;
+ case UDESCSUB_AC_OUTPUT:
+ uaudio_add_output(sc, iot, i);
+ break;
+ case UDESCSUB_AC_MIXER:
+ uaudio_add_mixer(sc, iot, i);
+ break;
+ case UDESCSUB_AC_SELECTOR:
+ uaudio_add_selector(sc, iot, i);
+ break;
+ case UDESCSUB_AC_FEATURE:
+ uaudio_add_feature(sc, iot, i);
+ break;
+ case UDESCSUB_AC_PROCESSING:
+ uaudio_add_processing(sc, iot, i);
+ break;
+ case UDESCSUB_AC_EXTENSION:
+ uaudio_add_extension(sc, iot, i);
+ break;
+ default:
+ printf("uaudio_identify_ac: bad AC desc subtype=0x%02x\n",
+ dp->bDescriptorSubtype);
+ break;
+ }
+ }
+
+ /* delete io_terminal */
+ for (i = 0; i < 256; i++) {
+ if (iot[i].d.desc == NULL)
+ continue;
+ if (iot[i].inputs != NULL) {
+ for (j = 0; j < iot[i].inputs_size; j++) {
+ if (iot[i].inputs[j] != NULL)
+ free(iot[i].inputs[j], M_TEMP);
+ }
+ free(iot[i].inputs, M_TEMP);
+ }
+ if (iot[i].output != NULL)
+ free(iot[i].output, M_TEMP);
+ iot[i].d.desc = NULL;
+ }
+ free(iot, M_TEMP);
+
+ return USBD_NORMAL_COMPLETION;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int
+uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
+{
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+ int n, nctls, i;
+
+ sc = addr;
+ DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index));
+ if (sc->sc_dying)
+ return EIO;
+
+ n = mi->index;
+ nctls = sc->sc_nctls;
+
+ switch (n) {
+ case UAC_OUTPUT:
+ mi->type = AUDIO_MIXER_CLASS;
+ mi->mixer_class = UAC_OUTPUT;
+ mi->next = mi->prev = AUDIO_MIXER_LAST;
+ strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name));
+ return 0;
+ case UAC_INPUT:
+ mi->type = AUDIO_MIXER_CLASS;
+ mi->mixer_class = UAC_INPUT;
+ mi->next = mi->prev = AUDIO_MIXER_LAST;
+ strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name));
+ return 0;
+ case UAC_EQUAL:
+ mi->type = AUDIO_MIXER_CLASS;
+ mi->mixer_class = UAC_EQUAL;
+ mi->next = mi->prev = AUDIO_MIXER_LAST;
+ strlcpy(mi->label.name, AudioCequalization,
+ sizeof(mi->label.name));
+ return 0;
+ case UAC_RECORD:
+ mi->type = AUDIO_MIXER_CLASS;
+ mi->mixer_class = UAC_RECORD;
+ mi->next = mi->prev = AUDIO_MIXER_LAST;
+ strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name));
+ return 0;
+ default:
+ break;
+ }
+
+ n -= UAC_NCLASSES;
+ if (n < 0 || n >= nctls)
+ return ENXIO;
+
+ mc = &sc->sc_ctls[n];
+ strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name));
+ mi->mixer_class = mc->class;
+ mi->next = mi->prev = AUDIO_MIXER_LAST; /* XXX */
+ switch (mc->type) {
+ case MIX_ON_OFF:
+ mi->type = AUDIO_MIXER_ENUM;
+ mi->un.e.num_mem = 2;
+ strlcpy(mi->un.e.member[0].label.name, AudioNoff,
+ sizeof(mi->un.e.member[0].label.name));
+ mi->un.e.member[0].ord = 0;
+ strlcpy(mi->un.e.member[1].label.name, AudioNon,
+ sizeof(mi->un.e.member[1].label.name));
+ mi->un.e.member[1].ord = 1;
+ break;
+ case MIX_SELECTOR:
+ mi->type = AUDIO_MIXER_ENUM;
+ mi->un.e.num_mem = mc->maxval - mc->minval + 1;
+ for (i = 0; i <= mc->maxval - mc->minval; i++) {
+ snprintf(mi->un.e.member[i].label.name,
+ sizeof(mi->un.e.member[i].label.name),
+ "%d", i + mc->minval);
+ mi->un.e.member[i].ord = i + mc->minval;
+ }
+ break;
+ default:
+ mi->type = AUDIO_MIXER_VALUE;
+ strlcpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
+ mi->un.v.num_channels = mc->nchan;
+ mi->un.v.delta = mc->delta;
+ break;
+ }
+ return 0;
+}
+
+static int
+uaudio_open(void *addr, int flags)
+{
+ struct uaudio_softc *sc;
+
+ sc = addr;
+ DPRINTF(("uaudio_open: sc=%p\n", sc));
+ if (sc->sc_dying)
+ return EIO;
+
+ if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY))
+ return EACCES;
+ if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD))
+ return EACCES;
+
+ return 0;
+}
+
+/*
+ * Close function is called at splaudio().
+ */
+static void
+uaudio_close(void *addr)
+{
+}
+
+static int
+uaudio_drain(void *addr)
+{
+ struct uaudio_softc *sc;
+
+ sc = addr;
+ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES);
+
+ return 0;
+}
+
+static int
+uaudio_halt_out_dma(void *addr)
+{
+ struct uaudio_softc *sc;
+
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ DPRINTF(("uaudio_halt_out_dma: enter\n"));
+ if (sc->sc_playchan.pipe != NULL) {
+ uaudio_chan_close(sc, &sc->sc_playchan);
+ sc->sc_playchan.pipe = NULL;
+ uaudio_chan_free_buffers(sc, &sc->sc_playchan);
+ sc->sc_playchan.intr = NULL;
+ }
+ return 0;
+}
+
+static int
+uaudio_halt_in_dma(void *addr)
+{
+ struct uaudio_softc *sc;
+
+ DPRINTF(("uaudio_halt_in_dma: enter\n"));
+ sc = addr;
+ if (sc->sc_recchan.pipe != NULL) {
+ uaudio_chan_close(sc, &sc->sc_recchan);
+ sc->sc_recchan.pipe = NULL;
+ uaudio_chan_free_buffers(sc, &sc->sc_recchan);
+ sc->sc_recchan.intr = NULL;
+ }
+ return 0;
+}
+
+static int
+uaudio_getdev(void *addr, struct audio_device *retp)
+{
+ struct uaudio_softc *sc;
+
+ DPRINTF(("uaudio_mixer_getdev:\n"));
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ *retp = uaudio_device;
+ return 0;
+}
+
+/*
+ * Make sure the block size is large enough to hold all outstanding transfers.
+ */
+static int
+uaudio_round_blocksize(void *addr, int blk)
+{
+ struct uaudio_softc *sc;
+ int b;
+
+ sc = addr;
+ DPRINTF(("uaudio_round_blocksize: blk=%d mode=%s\n", blk,
+ mode == AUMODE_PLAY ? "AUMODE_PLAY" : "AUMODE_RECORD"));
+
+ /* chan.bytes_per_frame can be 0. */
+ if (mode == AUMODE_PLAY || sc->sc_recchan.bytes_per_frame <= 0) {
+ b = param->sample_rate * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS;
+
+ /*
+ * This does not make accurate value in the case
+ * of b % USB_FRAMES_PER_SECOND != 0
+ */
+ b /= USB_FRAMES_PER_SECOND;
+
+ b *= param->precision / 8 * param->channels;
+ } else {
+ /*
+ * use wMaxPacketSize in bytes_per_frame.
+ * See uaudio_set_params() and uaudio_chan_init()
+ */
+ b = sc->sc_recchan.bytes_per_frame
+ * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS;
+ }
+
+ if (b <= 0)
+ b = 1;
+ blk = blk <= b ? b : blk / b * b;
+
+#ifdef DIAGNOSTIC
+ if (blk <= 0) {
+ printf("uaudio_round_blocksize: blk=%d\n", blk);
+ blk = 512;
+ }
+#endif
+
+ DPRINTF(("uaudio_round_blocksize: resultant blk=%d\n", blk));
+ return blk;
+}
+
+static int
+uaudio_get_props(void *addr)
+{
+ return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT;
+
+}
+#endif /* NetBSD or OpenBSD */
+
+static int
+uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue,
+ int wIndex, int len)
+{
+ usb_device_request_t req;
+ uint8_t data[4];
+ usbd_status err;
+ int val;
+
+#if defined(__FreeBSD__)
+ if (sc->sc_dying)
+ return EIO;
+#endif
+
+ if (wValue == -1)
+ return 0;
+
+ req.bmRequestType = type;
+ req.bRequest = which;
+ USETW(req.wValue, wValue);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, len);
+ DPRINTFN(2,("uaudio_get: type=0x%02x req=0x%02x wValue=0x%04x "
+ "wIndex=0x%04x len=%d\n",
+ type, which, wValue, wIndex, len));
+#if defined(__FreeBSD__)
+ if (sc->async != 0)
+ err = usbd_do_request_async(sc->sc_udev, &req, data);
+ else
+#endif
+ err = usbd_do_request(sc->sc_udev, &req, data);
+ if (err) {
+ DPRINTF(("uaudio_get: err=%s\n", usbd_errstr(err)));
+ return -1;
+ }
+ switch (len) {
+ case 1:
+ val = data[0];
+ break;
+ case 2:
+ val = data[0] | (data[1] << 8);
+ break;
+ default:
+ DPRINTF(("uaudio_get: bad length=%d\n", len));
+ return -1;
+ }
+ DPRINTFN(2,("uaudio_get: val=%d\n", val));
+ return val;
+}
+
+static void
+uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue,
+ int wIndex, int len, int val)
+{
+ usb_device_request_t req;
+ uint8_t data[4];
+ usbd_status err;
+
+#if defined(__FreeBSD__)
+ if (sc->sc_dying)
+ return;
+#endif
+
+ if (wValue == -1)
+ return;
+
+ req.bmRequestType = type;
+ req.bRequest = which;
+ USETW(req.wValue, wValue);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, len);
+ switch (len) {
+ case 1:
+ data[0] = val;
+ break;
+ case 2:
+ data[0] = val;
+ data[1] = val >> 8;
+ break;
+ default:
+ return;
+ }
+ DPRINTFN(2,("uaudio_set: type=0x%02x req=0x%02x wValue=0x%04x "
+ "wIndex=0x%04x len=%d, val=%d\n",
+ type, which, wValue, wIndex, len, val & 0xffff));
+#if defined(__FreeBSD__)
+ if (sc->async != 0)
+ err = usbd_do_request_async(sc->sc_udev, &req, data);
+ else
+#endif
+ err = usbd_do_request(sc->sc_udev, &req, data);
+#ifdef USB_DEBUG
+ if (err)
+ DPRINTF(("uaudio_set: err=%d\n", err));
+#endif
+}
+
+static int
+uaudio_signext(int type, int val)
+{
+ if (!MIX_UNSIGNED(type)) {
+ if (MIX_SIZE(type) == 2)
+ val = (int16_t)val;
+ else
+ val = (int8_t)val;
+ }
+ return val;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int
+uaudio_value2bsd(struct mixerctl *mc, int val)
+{
+ DPRINTFN(5, ("uaudio_value2bsd: type=%03x val=%d min=%d max=%d ",
+ mc->type, val, mc->minval, mc->maxval));
+ if (mc->type == MIX_ON_OFF) {
+ val = (val != 0);
+ } else if (mc->type == MIX_SELECTOR) {
+ if (val < mc->minval || val > mc->maxval)
+ val = mc->minval;
+ } else
+ val = ((uaudio_signext(mc->type, val) - mc->minval) * 255
+ + mc->mul/2) / mc->mul;
+ DPRINTFN(5, ("val'=%d\n", val));
+ return val;
+}
+#endif
+
+int
+uaudio_bsd2value(struct mixerctl *mc, int val)
+{
+ DPRINTFN(5,("uaudio_bsd2value: type=%03x val=%d min=%d max=%d ",
+ mc->type, val, mc->minval, mc->maxval));
+ if (mc->type == MIX_ON_OFF) {
+ val = (val != 0);
+ } else if (mc->type == MIX_SELECTOR) {
+ if (val < mc->minval || val > mc->maxval)
+ val = mc->minval;
+ } else
+ val = (val + mc->delta/2) * mc->mul / 255 + mc->minval;
+ DPRINTFN(5, ("val'=%d\n", val));
+ return val;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int
+uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc,
+ int chan)
+{
+ int val;
+
+ DPRINTFN(5,("uaudio_ctl_get: which=%d chan=%d\n", which, chan));
+ val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan],
+ mc->wIndex, MIX_SIZE(mc->type));
+ return uaudio_value2bsd(mc, val);
+}
+#endif
+
+static void
+uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc,
+ int chan, int val)
+{
+ val = uaudio_bsd2value(mc, val);
+ uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan],
+ mc->wIndex, MIX_SIZE(mc->type), val);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static int
+uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp)
+{
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+ int i, n, vals[MIX_MAX_CHAN], val;
+
+ DPRINTFN(2,("uaudio_mixer_get_port: index=%d\n", cp->dev));
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ n = cp->dev - UAC_NCLASSES;
+ if (n < 0 || n >= sc->sc_nctls)
+ return ENXIO;
+ mc = &sc->sc_ctls[n];
+
+ if (mc->type == MIX_ON_OFF) {
+ if (cp->type != AUDIO_MIXER_ENUM)
+ return EINVAL;
+ cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
+ } else if (mc->type == MIX_SELECTOR) {
+ if (cp->type != AUDIO_MIXER_ENUM)
+ return EINVAL;
+ cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
+ } else {
+ if (cp->type != AUDIO_MIXER_VALUE)
+ return (EINVAL);
+ if (cp->un.value.num_channels != 1 &&
+ cp->un.value.num_channels != mc->nchan)
+ return EINVAL;
+ for (i = 0; i < mc->nchan; i++)
+ vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i);
+ if (cp->un.value.num_channels == 1 && mc->nchan != 1) {
+ for (val = 0, i = 0; i < mc->nchan; i++)
+ val += vals[i];
+ vals[0] = val / mc->nchan;
+ }
+ for (i = 0; i < cp->un.value.num_channels; i++)
+ cp->un.value.level[i] = vals[i];
+ }
+
+ return 0;
+}
+
+static int
+uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp)
+{
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+ int i, n, vals[MIX_MAX_CHAN];
+
+ DPRINTFN(2,("uaudio_mixer_set_port: index = %d\n", cp->dev));
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ n = cp->dev - UAC_NCLASSES;
+ if (n < 0 || n >= sc->sc_nctls)
+ return ENXIO;
+ mc = &sc->sc_ctls[n];
+
+ if (mc->type == MIX_ON_OFF) {
+ if (cp->type != AUDIO_MIXER_ENUM)
+ return EINVAL;
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
+ } else if (mc->type == MIX_SELECTOR) {
+ if (cp->type != AUDIO_MIXER_ENUM)
+ return EINVAL;
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
+ } else {
+ if (cp->type != AUDIO_MIXER_VALUE)
+ return EINVAL;
+ if (cp->un.value.num_channels == 1)
+ for (i = 0; i < mc->nchan; i++)
+ vals[i] = cp->un.value.level[0];
+ else if (cp->un.value.num_channels == mc->nchan)
+ for (i = 0; i < mc->nchan; i++)
+ vals[i] = cp->un.value.level[i];
+ else
+ return EINVAL;
+ for (i = 0; i < mc->nchan; i++)
+ uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]);
+ }
+ return 0;
+}
+
+static int
+uaudio_trigger_input(void *addr, void *start, void *end, int blksize,
+ void (*intr)(void *), void *arg,
+ struct audio_params *param)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ usbd_status err;
+ int i, s;
+
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ DPRINTFN(3,("uaudio_trigger_input: sc=%p start=%p end=%p "
+ "blksize=%d\n", sc, start, end, blksize));
+ ch = &sc->sc_recchan;
+ uaudio_chan_set_param(ch, start, end, blksize);
+ DPRINTFN(3,("uaudio_trigger_input: sample_size=%d bytes/frame=%d "
+ "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
+ ch->fraction));
+
+ err = uaudio_chan_alloc_buffers(sc, ch);
+ if (err)
+ return EIO;
+
+ err = uaudio_chan_open(sc, ch);
+ if (err) {
+ uaudio_chan_free_buffers(sc, ch);
+ return EIO;
+ }
+
+ ch->intr = intr;
+ ch->arg = arg;
+
+ s = splusb();
+ for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */
+ uaudio_chan_rtransfer(ch);
+ splx(s);
+
+ return 0;
+}
+
+static int
+uaudio_trigger_output(void *addr, void *start, void *end, int blksize,
+ void (*intr)(void *), void *arg,
+ struct audio_params *param)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ usbd_status err;
+ int i, s;
+
+ sc = addr;
+ if (sc->sc_dying)
+ return EIO;
+
+ DPRINTFN(3,("uaudio_trigger_output: sc=%p start=%p end=%p "
+ "blksize=%d\n", sc, start, end, blksize));
+ ch = &sc->sc_playchan;
+ uaudio_chan_set_param(ch, start, end, blksize);
+ DPRINTFN(3,("uaudio_trigger_output: sample_size=%d bytes/frame=%d "
+ "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
+ ch->fraction));
+
+ err = uaudio_chan_alloc_buffers(sc, ch);
+ if (err)
+ return EIO;
+
+ err = uaudio_chan_open(sc, ch);
+ if (err) {
+ uaudio_chan_free_buffers(sc, ch);
+ return EIO;
+ }
+
+ ch->intr = intr;
+ ch->arg = arg;
+
+ s = splusb();
+ for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */
+ uaudio_chan_ptransfer(ch);
+ splx(s);
+
+ return 0;
+}
+#endif /* NetBSD or OpenBSD */
+
+/* Set up a pipe for a channel. */
+static usbd_status
+uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch)
+{
+ struct as_info *as;
+ int endpt;
+#if defined(__FreeBSD__)
+ int locked;
+#endif
+ usbd_status err;
+
+#if defined(__FreeBSD__)
+ if (sc->sc_dying)
+ return EIO;
+#endif
+
+ as = &sc->sc_alts[ch->altidx];
+ endpt = as->edesc->bEndpointAddress;
+ DPRINTF(("uaudio_chan_open: endpt=0x%02x, speed=%d, alt=%d\n",
+ endpt, ch->sample_rate, as->alt));
+
+#if defined(__FreeBSD__)
+ locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0;
+ if (locked)
+ CHN_UNLOCK(ch->pcm_ch);
+#endif
+ /* Set alternate interface corresponding to the mode. */
+ err = usbd_set_interface(as->ifaceh, as->alt);
+#if defined(__FreeBSD__)
+ if (locked)
+ CHN_LOCK(ch->pcm_ch);
+#endif
+ if (err)
+ return err;
+
+ /*
+ * If just one sampling rate is supported,
+ * no need to call uaudio_set_speed().
+ * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
+ */
+ if (as->asf1desc->bSamFreqType != 1) {
+ err = uaudio_set_speed(sc, endpt, ch->sample_rate);
+ if (err) {
+ DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n",
+ usbd_errstr(err)));
+ }
+ }
+
+ ch->pipe = 0;
+ ch->sync_pipe = 0;
+ DPRINTF(("uaudio_chan_open: create pipe to 0x%02x\n", endpt));
+ err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe);
+ if (err)
+ return err;
+ if (as->edesc1 != NULL) {
+ endpt = as->edesc1->bEndpointAddress;
+ DPRINTF(("uaudio_chan_open: create sync-pipe to 0x%02x\n", endpt));
+ err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe);
+ }
+ return err;
+}
+
+static void
+uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch)
+{
+ struct as_info *as;
+#if defined(__FreeBSD__)
+ int locked;
+
+ if (sc->sc_dying)
+ return ;
+#endif
+
+ as = &sc->sc_alts[ch->altidx];
+ as->sc_busy = 0;
+#if defined(__FreeBSD__)
+ locked = (ch->pcm_ch != NULL && mtx_owned(ch->pcm_ch->lock)) ? 1 : 0;
+ if (locked)
+ CHN_UNLOCK(ch->pcm_ch);
+#endif
+ if (sc->sc_nullalt >= 0) {
+ DPRINTF(("uaudio_chan_close: set null alt=%d\n",
+ sc->sc_nullalt));
+ /*
+ * The interface will be initialized later again, so an
+ * error does not hurt.
+ */
+ (void)usbd_set_interface(as->ifaceh, sc->sc_nullalt);
+ }
+ if (ch->pipe) {
+ usbd_abort_pipe(ch->pipe);
+ usbd_close_pipe(ch->pipe);
+ }
+ if (ch->sync_pipe) {
+ usbd_abort_pipe(ch->sync_pipe);
+ usbd_close_pipe(ch->sync_pipe);
+ }
+#if defined(__FreeBSD__)
+ if (locked)
+ CHN_LOCK(ch->pcm_ch);
+#endif
+}
+
+static usbd_status
+uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch)
+{
+ usbd_xfer_handle xfer;
+ void *buf;
+ int i, size;
+
+ size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES;
+ for (i = 0; i < UAUDIO_NCHANBUFS; i++) {
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == 0)
+ goto bad;
+ ch->chanbufs[i].xfer = xfer;
+ buf = usbd_alloc_buffer(xfer, size);
+ if (buf == 0) {
+ i++;
+ goto bad;
+ }
+ ch->chanbufs[i].buffer = buf;
+ ch->chanbufs[i].chan = ch;
+ }
+
+ return USBD_NORMAL_COMPLETION;
+
+bad:
+ while (--i >= 0)
+ /* implicit buffer free */
+ usbd_free_xfer(ch->chanbufs[i].xfer);
+ return USBD_NOMEM;
+}
+
+static void
+uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch)
+{
+ int i;
+
+ for (i = 0; i < UAUDIO_NCHANBUFS; i++)
+ usbd_free_xfer(ch->chanbufs[i].xfer);
+}
+
+/* Called at splusb() */
+static void
+uaudio_chan_ptransfer(struct chan *ch)
+{
+ struct chanbuf *cb;
+ int i, n, size, residue, total;
+
+ if (ch->sc->sc_dying)
+ return;
+
+ /* Pick the next channel buffer. */
+ cb = &ch->chanbufs[ch->curchanbuf];
+ if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
+ ch->curchanbuf = 0;
+
+ /* Compute the size of each frame in the next transfer. */
+ residue = ch->residue;
+ total = 0;
+ for (i = 0; i < UAUDIO_NFRAMES; i++) {
+ size = ch->bytes_per_frame;
+ residue += ch->fraction;
+ if (residue >= USB_FRAMES_PER_SECOND) {
+ if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
+ size += ch->sample_size;
+ residue -= USB_FRAMES_PER_SECOND;
+ }
+ cb->sizes[i] = size;
+ total += size;
+ }
+ ch->residue = residue;
+ cb->size = total;
+
+ /*
+ * Transfer data from upper layer buffer to channel buffer, taking
+ * care of wrapping the upper layer buffer.
+ */
+ n = min(total, ch->end - ch->cur);
+ memcpy(cb->buffer, ch->cur, n);
+ ch->cur += n;
+ if (ch->cur >= ch->end)
+ ch->cur = ch->start;
+ if (total > n) {
+ total -= n;
+ memcpy(cb->buffer + n, ch->cur, total);
+ ch->cur += total;
+ }
+
+#ifdef USB_DEBUG
+ if (uaudiodebug > 8) {
+ DPRINTF(("uaudio_chan_ptransfer: buffer=%p, residue=0.%03d\n",
+ cb->buffer, ch->residue));
+ for (i = 0; i < UAUDIO_NFRAMES; i++) {
+ DPRINTF((" [%d] length %d\n", i, cb->sizes[i]));
+ }
+ }
+#endif
+
+ DPRINTFN(5,("uaudio_chan_transfer: ptransfer xfer=%p\n", cb->xfer));
+ /* Fill the request */
+ usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
+ UAUDIO_NFRAMES, USBD_NO_COPY,
+ uaudio_chan_pintr);
+
+ (void)usbd_transfer(cb->xfer);
+}
+
+static void
+uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct chanbuf *cb;
+ struct chan *ch;
+ u_int32_t count;
+ int s;
+
+ cb = priv;
+ ch = cb->chan;
+ /* Return if we are aborting. */
+ if (status == USBD_CANCELLED)
+ return;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ DPRINTFN(5,("uaudio_chan_pintr: count=%d, transferred=%d\n",
+ count, ch->transferred));
+#ifdef DIAGNOSTIC
+ if (count != cb->size) {
+ printf("uaudio_chan_pintr: count(%d) != size(%d)\n",
+ count, cb->size);
+ }
+#endif
+
+ ch->transferred += cb->size;
+#if defined(__FreeBSD__)
+ /* s = spltty(); */
+ s = splhigh();
+ chn_intr(ch->pcm_ch);
+ splx(s);
+#else
+ s = splaudio();
+ /* Call back to upper layer */
+ while (ch->transferred >= ch->blksize) {
+ ch->transferred -= ch->blksize;
+ DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n",
+ ch->intr, ch->arg));
+ ch->intr(ch->arg);
+ }
+ splx(s);
+#endif
+
+ /* start next transfer */
+ uaudio_chan_ptransfer(ch);
+}
+
+/* Called at splusb() */
+static void
+uaudio_chan_rtransfer(struct chan *ch)
+{
+ struct chanbuf *cb;
+ int i, size, residue, total;
+
+ if (ch->sc->sc_dying)
+ return;
+
+ /* Pick the next channel buffer. */
+ cb = &ch->chanbufs[ch->curchanbuf];
+ if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
+ ch->curchanbuf = 0;
+
+ /* Compute the size of each frame in the next transfer. */
+ residue = ch->residue;
+ total = 0;
+ for (i = 0; i < UAUDIO_NFRAMES; i++) {
+ size = ch->bytes_per_frame;
+ cb->sizes[i] = size;
+ cb->offsets[i] = total;
+ total += size;
+ }
+ ch->residue = residue;
+ cb->size = total;
+
+#ifdef USB_DEBUG
+ if (uaudiodebug > 8) {
+ DPRINTF(("uaudio_chan_rtransfer: buffer=%p, residue=0.%03d\n",
+ cb->buffer, ch->residue));
+ for (i = 0; i < UAUDIO_NFRAMES; i++) {
+ DPRINTF((" [%d] length %d\n", i, cb->sizes[i]));
+ }
+ }
+#endif
+
+ DPRINTFN(5,("uaudio_chan_rtransfer: transfer xfer=%p\n", cb->xfer));
+ /* Fill the request */
+ usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
+ UAUDIO_NFRAMES, USBD_NO_COPY,
+ uaudio_chan_rintr);
+
+ (void)usbd_transfer(cb->xfer);
+}
+
+static void
+uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct chanbuf *cb = priv;
+ struct chan *ch = cb->chan;
+ u_int32_t count;
+ int s, i, n, frsize;
+
+ /* Return if we are aborting. */
+ if (status == USBD_CANCELLED)
+ return;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n",
+ count, ch->transferred));
+
+ /* count < cb->size is normal for asynchronous source */
+#ifdef DIAGNOSTIC
+ if (count > cb->size) {
+ printf("uaudio_chan_rintr: count(%d) > size(%d)\n",
+ count, cb->size);
+ }
+#endif
+
+ /*
+ * Transfer data from channel buffer to upper layer buffer, taking
+ * care of wrapping the upper layer buffer.
+ */
+ for(i = 0; i < UAUDIO_NFRAMES; i++) {
+ frsize = cb->sizes[i];
+ n = min(frsize, ch->end - ch->cur);
+ memcpy(ch->cur, cb->buffer + cb->offsets[i], n);
+ ch->cur += n;
+ if (ch->cur >= ch->end)
+ ch->cur = ch->start;
+ if (frsize > n) {
+ memcpy(ch->cur, cb->buffer + cb->offsets[i] + n,
+ frsize - n);
+ ch->cur += frsize - n;
+ }
+ }
+
+ /* Call back to upper layer */
+ ch->transferred += count;
+#if defined(__FreeBSD__)
+ s = spltty();
+ chn_intr(ch->pcm_ch);
+ splx(s);
+#else
+ s = splaudio();
+ while (ch->transferred >= ch->blksize) {
+ ch->transferred -= ch->blksize;
+ DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n",
+ ch->intr, ch->arg));
+ ch->intr(ch->arg);
+ }
+ splx(s);
+#endif
+
+ /* start next transfer */
+ uaudio_chan_rtransfer(ch);
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+static void
+uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param,
+ int maxpktsize)
+{
+ int samples_per_frame, sample_size;
+
+ ch->altidx = altidx;
+ sample_size = param->precision * param->factor * param->hw_channels / 8;
+ samples_per_frame = param->hw_sample_rate / USB_FRAMES_PER_SECOND;
+ ch->sample_size = sample_size;
+ ch->sample_rate = param->hw_sample_rate;
+ if (maxpktsize == 0) {
+ ch->fraction = param->hw_sample_rate % USB_FRAMES_PER_SECOND;
+ ch->bytes_per_frame = samples_per_frame * sample_size;
+ } else {
+ ch->fraction = 0;
+ ch->bytes_per_frame = maxpktsize;
+ }
+ ch->residue = 0;
+}
+
+static void
+uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize)
+{
+ ch->start = start;
+ ch->end = end;
+ ch->cur = start;
+ ch->blksize = blksize;
+ ch->transferred = 0;
+ ch->curchanbuf = 0;
+}
+
+static void
+uaudio_get_minmax_rates(int nalts, const struct as_info *alts,
+ const struct audio_params *p, int mode,
+ u_long *min, u_long *max)
+{
+ const struct usb_audio_streaming_type1_descriptor *a1d;
+ int i, j;
+
+ *min = ULONG_MAX;
+ *max = 0;
+ for (i = 0; i < nalts; i++) {
+ a1d = alts[i].asf1desc;
+ if (alts[i].sc_busy)
+ continue;
+ if (p->hw_channels != a1d->bNrChannels)
+ continue;
+ if (p->hw_precision != a1d->bBitResolution)
+ continue;
+ if (p->hw_encoding != alts[i].encoding)
+ continue;
+ if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress))
+ continue;
+ if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+ DPRINTFN(2,("uaudio_get_minmax_rates: cont %d-%d\n",
+ UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
+ if (UA_SAMP_LO(a1d) < *min)
+ *min = UA_SAMP_LO(a1d);
+ if (UA_SAMP_HI(a1d) > *max)
+ *max = UA_SAMP_HI(a1d);
+ } else {
+ for (j = 0; j < a1d->bSamFreqType; j++) {
+ DPRINTFN(2,("uaudio_get_minmax_rates: disc #%d: %d\n",
+ j, UA_GETSAMP(a1d, j)));
+ if (UA_GETSAMP(a1d, j) < *min)
+ *min = UA_GETSAMP(a1d, j);
+ if (UA_GETSAMP(a1d, j) > *max)
+ *max = UA_GETSAMP(a1d, j);
+ }
+ }
+ }
+}
+
+static int
+uaudio_match_alt_sub(int nalts, const struct as_info *alts,
+ const struct audio_params *p, int mode, u_long rate)
+{
+ const struct usb_audio_streaming_type1_descriptor *a1d;
+ int i, j;
+
+ DPRINTF(("uaudio_match_alt_sub: search for %luHz %dch\n",
+ rate, p->hw_channels));
+ for (i = 0; i < nalts; i++) {
+ a1d = alts[i].asf1desc;
+ if (alts[i].sc_busy)
+ continue;
+ if (p->hw_channels != a1d->bNrChannels)
+ continue;
+ if (p->hw_precision != a1d->bBitResolution)
+ continue;
+ if (p->hw_encoding != alts[i].encoding)
+ continue;
+ if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress))
+ continue;
+ if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+ DPRINTFN(3,("uaudio_match_alt_sub: cont %d-%d\n",
+ UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
+ if (UA_SAMP_LO(a1d) <= rate && rate <= UA_SAMP_HI(a1d))
+ return i;
+ } else {
+ for (j = 0; j < a1d->bSamFreqType; j++) {
+ DPRINTFN(3,("uaudio_match_alt_sub: disc #%d: %d\n",
+ j, UA_GETSAMP(a1d, j)));
+ /* XXX allow for some slack */
+ if (UA_GETSAMP(a1d, j) == rate)
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static int
+uaudio_match_alt_chan(int nalts, const struct as_info *alts,
+ struct audio_params *p, int mode)
+{
+ int i, n;
+ u_long min, max;
+ u_long rate;
+
+ /* Exact match */
+ DPRINTF(("uaudio_match_alt_chan: examine %ldHz %dch %dbit.\n",
+ p->sample_rate, p->hw_channels, p->hw_precision));
+ i = uaudio_match_alt_sub(nalts, alts, p, mode, p->sample_rate);
+ if (i >= 0)
+ return i;
+
+ uaudio_get_minmax_rates(nalts, alts, p, mode, &min, &max);
+ DPRINTF(("uaudio_match_alt_chan: min=%lu max=%lu\n", min, max));
+ if (max <= 0)
+ return -1;
+ /* Search for biggers */
+ n = 2;
+ while ((rate = p->sample_rate * n++) <= max) {
+ i = uaudio_match_alt_sub(nalts, alts, p, mode, rate);
+ if (i >= 0) {
+ p->hw_sample_rate = rate;
+ return i;
+ }
+ }
+ if (p->sample_rate >= min) {
+ i = uaudio_match_alt_sub(nalts, alts, p, mode, max);
+ if (i >= 0) {
+ p->hw_sample_rate = max;
+ return i;
+ }
+ } else {
+ i = uaudio_match_alt_sub(nalts, alts, p, mode, min);
+ if (i >= 0) {
+ p->hw_sample_rate = min;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+uaudio_match_alt(int nalts, const struct as_info *alts,
+ struct audio_params *p, int mode)
+{
+ int i, n;
+
+ mode = mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN;
+ i = uaudio_match_alt_chan(nalts, alts, p, mode);
+ if (i >= 0)
+ return i;
+
+ for (n = p->channels + 1; n <= AUDIO_MAX_CHANNELS; n++) {
+ p->hw_channels = n;
+ i = uaudio_match_alt_chan(nalts, alts, p, mode);
+ if (i >= 0)
+ return i;
+ }
+
+ if (p->channels != 2)
+ return -1;
+ p->hw_channels = 1;
+ return uaudio_match_alt_chan(nalts, alts, p, mode);
+}
+
+static int
+uaudio_set_params(void *addr, int setmode, int usemode,
+ struct audio_params *play, struct audio_params *rec)
+{
+ struct uaudio_softc *sc;
+ int flags;
+ int factor;
+ int enc, i;
+ int paltidx, raltidx;
+ void (*swcode)(void *, u_char *buf, int cnt);
+ struct audio_params *p;
+ int mode;
+
+ sc = addr;
+ flags = sc->sc_altflags;
+ paltidx = -1;
+ raltidx = -1;
+ if (sc->sc_dying)
+ return EIO;
+
+ if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) ||
+ ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL))
+ return EBUSY;
+
+ if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
+ sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0;
+ if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
+ sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0;
+
+ /* Some uaudio devices are unidirectional. Don't try to find a
+ matching mode for the unsupported direction. */
+ setmode &= sc->sc_mode;
+
+ for (mode = AUMODE_RECORD; mode != -1;
+ mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
+ if ((setmode & mode) == 0)
+ continue;
+
+ p = (mode == AUMODE_PLAY) ? play : rec;
+
+ factor = 1;
+ swcode = 0;
+ enc = p->encoding;
+ switch (enc) {
+ case AUDIO_ENCODING_SLINEAR_BE:
+ /* FALLTHROUGH */
+ case AUDIO_ENCODING_SLINEAR_LE:
+ if (enc == AUDIO_ENCODING_SLINEAR_BE
+ && p->precision == 16 && (flags & HAS_16)) {
+ swcode = swap_bytes;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ } else if (p->precision == 8) {
+ if (flags & HAS_8) {
+ /* No conversion */
+ } else if (flags & HAS_8U) {
+ swcode = change_sign8;
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ } else if (flags & HAS_16) {
+ factor = 2;
+ p->hw_precision = 16;
+ if (mode == AUMODE_PLAY)
+ swcode = linear8_to_linear16_le;
+ else
+ swcode = linear16_to_linear8_le;
+ }
+ }
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ /* FALLTHROUGH */
+ case AUDIO_ENCODING_ULINEAR_LE:
+ if (p->precision == 16) {
+ if (enc == AUDIO_ENCODING_ULINEAR_LE)
+ swcode = change_sign16_le;
+ else if (mode == AUMODE_PLAY)
+ swcode = swap_bytes_change_sign16_le;
+ else
+ swcode = change_sign16_swap_bytes_le;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ } else if (p->precision == 8) {
+ if (flags & HAS_8U) {
+ /* No conversion */
+ } else if (flags & HAS_8) {
+ swcode = change_sign8;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ } else if (flags & HAS_16) {
+ factor = 2;
+ p->hw_precision = 16;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ if (mode == AUMODE_PLAY)
+ swcode = ulinear8_to_slinear16_le;
+ else
+ swcode = slinear16_to_ulinear8_le;
+ }
+ }
+ break;
+ case AUDIO_ENCODING_ULAW:
+ if (flags & HAS_MULAW)
+ break;
+ if (flags & HAS_16) {
+ if (mode == AUMODE_PLAY)
+ swcode = mulaw_to_slinear16_le;
+ else
+ swcode = slinear16_to_mulaw_le;
+ factor = 2;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ p->hw_precision = 16;
+ } else if (flags & HAS_8U) {
+ if (mode == AUMODE_PLAY)
+ swcode = mulaw_to_ulinear8;
+ else
+ swcode = ulinear8_to_mulaw;
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ } else if (flags & HAS_8) {
+ if (mode == AUMODE_PLAY)
+ swcode = mulaw_to_slinear8;
+ else
+ swcode = slinear8_to_mulaw;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ } else
+ return (EINVAL);
+ break;
+ case AUDIO_ENCODING_ALAW:
+ if (flags & HAS_ALAW)
+ break;
+ if (mode == AUMODE_PLAY && (flags & HAS_16)) {
+ swcode = alaw_to_slinear16_le;
+ factor = 2;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ p->hw_precision = 16;
+ } else if (flags & HAS_8U) {
+ if (mode == AUMODE_PLAY)
+ swcode = alaw_to_ulinear8;
+ else
+ swcode = ulinear8_to_alaw;
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ } else if (flags & HAS_8) {
+ if (mode == AUMODE_PLAY)
+ swcode = alaw_to_slinear8;
+ else
+ swcode = slinear8_to_alaw;
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ } else
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL);
+ }
+ /* XXX do some other conversions... */
+
+ DPRINTF(("uaudio_set_params: chan=%d prec=%d enc=%d rate=%ld\n",
+ p->channels, p->hw_precision, enc, p->sample_rate));
+
+ p->hw_encoding = enc;
+ i = uaudio_match_alt(sc->sc_nalts, sc->sc_alts, p, mode);
+ if (i < 0)
+ return (EINVAL);
+
+ p->sw_code = swcode;
+ p->factor = factor;
+
+ if (mode == AUMODE_PLAY)
+ paltidx = i;
+ else
+ raltidx = i;
+ }
+
+ if ((setmode & AUMODE_PLAY)) {
+ /* XXX abort transfer if currently happening? */
+ uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0);
+ }
+ if ((setmode & AUMODE_RECORD)) {
+ /* XXX abort transfer if currently happening? */
+ uaudio_chan_init(&sc->sc_recchan, raltidx, rec,
+ UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize));
+ }
+
+ if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
+ sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1;
+ if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
+ sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1;
+
+ DPRINTF(("uaudio_set_params: use altidx=p%d/r%d, altno=p%d/r%d\n",
+ sc->sc_playchan.altidx, sc->sc_recchan.altidx,
+ (sc->sc_playchan.altidx >= 0)
+ ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting
+ : -1,
+ (sc->sc_recchan.altidx >= 0)
+ ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting
+ : -1));
+
+ return 0;
+}
+#endif /* NetBSD or OpenBSD */
+
+static usbd_status
+uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed)
+{
+ usb_device_request_t req;
+ uint8_t data[3];
+
+ DPRINTFN(5,("uaudio_set_speed: endpt=%d speed=%u\n", endpt, speed));
+ req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
+ req.bRequest = SET_CUR;
+ USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
+ USETW(req.wIndex, endpt);
+ USETW(req.wLength, 3);
+ data[0] = speed;
+ data[1] = speed >> 8;
+ data[2] = speed >> 16;
+
+#if defined(__FreeBSD__)
+ if (sc->async != 0)
+ return usbd_do_request_async(sc->sc_udev, &req, data);
+#endif
+ return usbd_do_request(sc->sc_udev, &req, data);
+}
+
+
+#if defined(__FreeBSD__)
+/************************************************************/
+int
+uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode)
+{
+ int i, j, enc;
+ int samples_per_frame, sample_size;
+
+ if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL))
+ return (-1);
+
+ switch(ch->format & 0x000FFFFF) {
+ case AFMT_U8:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 8;
+ break;
+ case AFMT_S8:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 8;
+ break;
+ case AFMT_A_LAW: /* ? */
+ enc = AUDIO_ENCODING_ALAW;
+ ch->precision = 8;
+ break;
+ case AFMT_MU_LAW: /* ? */
+ enc = AUDIO_ENCODING_ULAW;
+ ch->precision = 8;
+ break;
+ case AFMT_S16_LE:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 16;
+ break;
+ case AFMT_S16_BE:
+ enc = AUDIO_ENCODING_SLINEAR_BE;
+ ch->precision = 16;
+ break;
+ case AFMT_U16_LE:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 16;
+ break;
+ case AFMT_U16_BE:
+ enc = AUDIO_ENCODING_ULINEAR_BE;
+ ch->precision = 16;
+ break;
+ case AFMT_S24_LE:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 24;
+ break;
+ case AFMT_S24_BE:
+ enc = AUDIO_ENCODING_SLINEAR_BE;
+ ch->precision = 24;
+ break;
+ case AFMT_U24_LE:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 24;
+ break;
+ case AFMT_U24_BE:
+ enc = AUDIO_ENCODING_ULINEAR_BE;
+ ch->precision = 24;
+ break;
+ case AFMT_S32_LE:
+ enc = AUDIO_ENCODING_SLINEAR_LE;
+ ch->precision = 32;
+ break;
+ case AFMT_S32_BE:
+ enc = AUDIO_ENCODING_SLINEAR_BE;
+ ch->precision = 32;
+ break;
+ case AFMT_U32_LE:
+ enc = AUDIO_ENCODING_ULINEAR_LE;
+ ch->precision = 32;
+ break;
+ case AFMT_U32_BE:
+ enc = AUDIO_ENCODING_ULINEAR_BE;
+ ch->precision = 32;
+ break;
+ default:
+ enc = 0;
+ ch->precision = 16;
+ printf("Unknown format %x\n", ch->format);
+ }
+
+ if (ch->format & AFMT_STEREO) {
+ ch->channels = 2;
+ } else {
+ ch->channels = 1;
+ }
+
+/* for (mode = ...... */
+ for (i = 0; i < sc->sc_nalts; i++) {
+ const struct usb_audio_streaming_type1_descriptor *a1d =
+ sc->sc_alts[i].asf1desc;
+ if (ch->channels == a1d->bNrChannels &&
+ ch->precision == a1d->bBitResolution &&
+#if 0
+ enc == sc->sc_alts[i].encoding) {
+#else
+ enc == sc->sc_alts[i].encoding &&
+ (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) ==
+ UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) {
+#endif
+ if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+ DPRINTFN(2,("uaudio_set_params: cont %d-%d\n",
+ UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
+ if (UA_SAMP_LO(a1d) <= ch->sample_rate &&
+ ch->sample_rate <= UA_SAMP_HI(a1d)) {
+ if (mode == AUMODE_PLAY)
+ sc->sc_playchan.altidx = i;
+ else
+ sc->sc_recchan.altidx = i;
+ goto found;
+ }
+ } else {
+ for (j = 0; j < a1d->bSamFreqType; j++) {
+ DPRINTFN(2,("uaudio_set_params: disc #"
+ "%d: %d\n", j, UA_GETSAMP(a1d, j)));
+ /* XXX allow for some slack */
+ if (UA_GETSAMP(a1d, j) ==
+ ch->sample_rate) {
+ if (mode == AUMODE_PLAY)
+ sc->sc_playchan.altidx = i;
+ else
+ sc->sc_recchan.altidx = i;
+ goto found;
+ }
+ }
+ }
+ }
+ }
+ /* return (EINVAL); */
+ if (mode == AUMODE_PLAY)
+ printf("uaudio: This device can't play in rate=%d.\n", ch->sample_rate);
+ else
+ printf("uaudio: This device can't record in rate=%d.\n", ch->sample_rate);
+ return (-1);
+
+ found:
+#if 0 /* XXX */
+ p->sw_code = swcode;
+ p->factor = factor;
+ if (usemode == mode)
+ sc->sc_curaltidx = i;
+#endif
+/* } */
+
+ sample_size = ch->precision * ch->channels / 8;
+ samples_per_frame = ch->sample_rate / USB_FRAMES_PER_SECOND;
+ ch->fraction = ch->sample_rate % USB_FRAMES_PER_SECOND;
+ ch->sample_size = sample_size;
+ ch->bytes_per_frame = samples_per_frame * sample_size;
+ ch->residue = 0;
+
+ ch->cur = ch->start;
+ ch->transferred = 0;
+ ch->curchanbuf = 0;
+ return (0);
+}
+
+struct uaudio_conversion {
+ uint8_t uaudio_fmt;
+ uint8_t uaudio_prec;
+ uint32_t freebsd_fmt;
+};
+
+const struct uaudio_conversion const accepted_conversion[] = {
+ {AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8},
+ {AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE},
+ {AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE},
+ {AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE},
+ {AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE},
+ {AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE},
+ {AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE},
+ {AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8},
+ {AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE},
+ {AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE},
+ {AUDIO_ENCODING_SLINEAR_LE, 32, AFMT_S32_LE},
+ {AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE},
+ {AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE},
+ {AUDIO_ENCODING_SLINEAR_BE, 32, AFMT_S32_BE},
+ {AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW},
+ {AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW},
+ {0,0,0}
+};
+
+unsigned
+uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap)
+{
+ struct uaudio_softc *sc;
+ const struct usb_audio_streaming_type1_descriptor *asf1d;
+ const struct uaudio_conversion *iterator;
+ unsigned fmtcount, foundcount;
+ u_int32_t fmt;
+ uint8_t format, numchan, subframesize, prec, dir, iscontinuous;
+ int freq, freq_min, freq_max;
+ char *numchannel_descr;
+ char freq_descr[64];
+ int i,r;
+
+ sc = device_get_softc(dev);
+ if (sc == NULL)
+ return 0;
+
+ cap->minspeed = cap->maxspeed = 0;
+ foundcount = fmtcount = 0;
+
+ for (i = 0; i < sc->sc_nalts; i++) {
+ dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
+
+ if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+ continue;
+
+ asf1d = sc->sc_alts[i].asf1desc;
+ format = sc->sc_alts[i].encoding;
+
+ numchan = asf1d->bNrChannels;
+ subframesize = asf1d->bSubFrameSize;
+ prec = asf1d->bBitResolution; /* precision */
+ iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
+
+ if (iscontinuous)
+ snprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ else
+ snprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType);
+
+ if (numchan == 1)
+ numchannel_descr = " (mono)";
+ else if (numchan == 2)
+ numchannel_descr = " (stereo)";
+ else
+ numchannel_descr = "";
+
+ if (bootverbose) {
+ device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n",
+ (dir==UE_DIR_OUT)?"playback":"record",
+ numchannel_descr, freq_descr,
+ prec, subframesize, numchan, subframesize*numchan);
+ }
+ /*
+ * Now start rejecting the ones that don't map to FreeBSD
+ */
+
+ if (numchan != 1 && numchan != 2)
+ continue;
+
+ for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+ if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec)
+ break;
+
+ if (iterator->uaudio_fmt == 0)
+ continue;
+
+ fmt = iterator->freebsd_fmt;
+
+ if (numchan == 2)
+ fmt |= AFMT_STEREO;
+
+ foundcount++;
+
+ if (fmtcount >= maxfmt)
+ continue;
+
+ cap->fmtlist[fmtcount++] = fmt;
+
+ if (iscontinuous) {
+ freq_min = UA_SAMP_LO(asf1d);
+ freq_max = UA_SAMP_HI(asf1d);
+
+ if (cap->minspeed == 0 || freq_min < cap->minspeed)
+ cap->minspeed = freq_min;
+ if (cap->maxspeed == 0)
+ cap->maxspeed = cap->minspeed;
+ if (freq_max > cap->maxspeed)
+ cap->maxspeed = freq_max;
+ } else {
+ for (r = 0; r < asf1d->bSamFreqType; r++) {
+ freq = UA_GETSAMP(asf1d, r);
+ if (cap->minspeed == 0 || freq < cap->minspeed)
+ cap->minspeed = freq;
+ if (cap->maxspeed == 0)
+ cap->maxspeed = cap->minspeed;
+ if (freq > cap->maxspeed)
+ cap->maxspeed = freq;
+ }
+ }
+ }
+ cap->fmtlist[fmtcount] = 0;
+ return foundcount;
+}
+
+void
+uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end,
+ struct pcm_channel *pc, int dir)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+
+ sc = device_get_softc(dev);
+#ifndef NO_RECORDING
+ if (dir == PCMDIR_PLAY)
+ ch = &sc->sc_playchan;
+ else
+ ch = &sc->sc_recchan;
+#else
+ ch = &sc->sc_playchan;
+#endif
+
+ ch->start = start;
+ ch->end = end;
+
+ ch->pcm_ch = pc;
+
+ return;
+}
+
+void
+uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+
+ sc = device_get_softc(dev);
+#ifndef NO_RECORDING
+ if (dir == PCMDIR_PLAY)
+ ch = &sc->sc_playchan;
+ else
+ ch = &sc->sc_recchan;
+#else
+ ch = &sc->sc_playchan;
+#endif
+
+ ch->blksize = blocksize;
+
+ return;
+}
+
+int
+uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir)
+{
+ const struct uaudio_conversion *iterator;
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ int i, r, score, hiscore, bestspeed;
+
+ sc = device_get_softc(dev);
+#ifndef NO_RECORDING
+ if (reqdir == PCMDIR_PLAY)
+ ch = &sc->sc_playchan;
+ else
+ ch = &sc->sc_recchan;
+#else
+ ch = &sc->sc_playchan;
+#endif
+ /*
+ * We are successful if we find an endpoint that matches our selected format and it
+ * supports the requested speed.
+ */
+ hiscore = 0;
+ bestspeed = 1;
+ for (i = 0; i < sc->sc_nalts; i++) {
+ int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
+ int format = sc->sc_alts[i].encoding;
+ const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc;
+ int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS;
+
+ if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY))
+ continue;
+
+ for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++)
+ if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff))
+ continue;
+ if (iscontinuous) {
+ if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) {
+ ch->sample_rate = speed;
+ return speed;
+ } else if (speed < UA_SAMP_LO(asf1d)) {
+ score = 0xfff * speed / UA_SAMP_LO(asf1d);
+ if (score > hiscore) {
+ bestspeed = UA_SAMP_LO(asf1d);
+ hiscore = score;
+ }
+ } else if (speed > UA_SAMP_HI(asf1d)) {
+ score = 0xfff * UA_SAMP_HI(asf1d) / speed;
+ if (score > hiscore) {
+ bestspeed = UA_SAMP_HI(asf1d);
+ hiscore = score;
+ }
+ }
+ continue;
+ }
+ for (r = 0; r < asf1d->bSamFreqType; r++) {
+ if (speed == UA_GETSAMP(asf1d, r)) {
+ ch->sample_rate = speed;
+ return speed;
+ }
+ if (speed > UA_GETSAMP(asf1d, r))
+ score = 0xfff * UA_GETSAMP(asf1d, r) / speed;
+ else
+ score = 0xfff * speed / UA_GETSAMP(asf1d, r);
+ if (score > hiscore) {
+ bestspeed = UA_GETSAMP(asf1d, r);
+ hiscore = score;
+ }
+ }
+ }
+ if (bestspeed != 1) {
+ ch->sample_rate = bestspeed;
+ return bestspeed;
+ }
+
+ return 0;
+}
+
+int
+uaudio_chan_getptr(device_t dev, int dir)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ int ptr;
+
+ sc = device_get_softc(dev);
+#ifndef NO_RECORDING
+ if (dir == PCMDIR_PLAY)
+ ch = &sc->sc_playchan;
+ else
+ ch = &sc->sc_recchan;
+#else
+ ch = &sc->sc_playchan;
+#endif
+
+ ptr = ch->cur - ch->start;
+
+ return ptr;
+}
+
+void
+uaudio_chan_set_param_format(device_t dev, u_int32_t format, int dir)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+
+ sc = device_get_softc(dev);
+#ifndef NO_RECORDING
+ if (dir == PCMDIR_PLAY)
+ ch = &sc->sc_playchan;
+ else
+ ch = &sc->sc_recchan;
+#else
+ ch = &sc->sc_playchan;
+#endif
+
+ ch->format = format;
+
+ return;
+}
+
+int
+uaudio_halt_out_dma(device_t dev)
+{
+ struct uaudio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF(("uaudio_halt_out_dma: enter\n"));
+ if (sc->sc_playchan.pipe != NULL) {
+ uaudio_chan_close(sc, &sc->sc_playchan);
+ sc->sc_playchan.pipe = 0;
+ uaudio_chan_free_buffers(sc, &sc->sc_playchan);
+ }
+ return (0);
+}
+
+int
+uaudio_halt_in_dma(device_t dev)
+{
+ struct uaudio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ DPRINTF(("uaudio_halt_in_dma: enter\n"));
+ if (sc->sc_recchan.pipe != NULL) {
+ uaudio_chan_close(sc, &sc->sc_recchan);
+ sc->sc_recchan.pipe = NULL;
+ uaudio_chan_free_buffers(sc, &sc->sc_recchan);
+/* sc->sc_recchan.intr = NULL; */
+ }
+ return (0);
+}
+
+int
+uaudio_trigger_input(device_t dev)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ usbd_status err;
+ int i, s;
+
+ sc = device_get_softc(dev);
+ ch = &sc->sc_recchan;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+/* uaudio_chan_set_param(ch, start, end, blksize) */
+ if (uaudio_init_params(sc, ch, AUMODE_RECORD))
+ return (EIO);
+
+ err = uaudio_chan_alloc_buffers(sc, ch);
+ if (err)
+ return (EIO);
+
+ err = uaudio_chan_open(sc, ch);
+ if (err) {
+ uaudio_chan_free_buffers(sc, ch);
+ return (EIO);
+ }
+
+/* ch->intr = intr;
+ ch->arg = arg; */
+
+ s = splusb();
+ for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */
+ uaudio_chan_rtransfer(ch);
+ splx(s);
+
+ return (0);
+}
+
+int
+uaudio_trigger_output(device_t dev)
+{
+ struct uaudio_softc *sc;
+ struct chan *ch;
+ usbd_status err;
+ int i, s;
+
+ sc = device_get_softc(dev);
+ ch = &sc->sc_playchan;
+
+ if (sc->sc_dying)
+ return (EIO);
+
+ if (uaudio_init_params(sc, ch, AUMODE_PLAY))
+ return (EIO);
+
+ err = uaudio_chan_alloc_buffers(sc, ch);
+ if (err)
+ return (EIO);
+
+ err = uaudio_chan_open(sc, ch);
+ if (err) {
+ uaudio_chan_free_buffers(sc, ch);
+ return (EIO);
+ }
+
+ s = splusb();
+ for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */
+ uaudio_chan_ptransfer(ch);
+ splx(s);
+
+ return (0);
+}
+
+u_int32_t
+uaudio_query_mix_info(device_t dev)
+{
+ int i;
+ u_int32_t mask = 0;
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+
+ sc = device_get_softc(dev);
+ for (i=0; i < sc->sc_nctls; i++) {
+ mc = &sc->sc_ctls[i];
+ if (mc->ctl != SOUND_MIXER_NRDEVICES) {
+ /* Set device mask bits.
+ See /usr/include/machine/soundcard.h */
+ mask |= (1 << mc->ctl);
+ }
+ }
+ return mask;
+}
+
+u_int32_t
+uaudio_query_recsrc_info(device_t dev)
+{
+ int i, rec_selector_id;
+ u_int32_t mask = 0;
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+
+ sc = device_get_softc(dev);
+ rec_selector_id = -1;
+ for (i=0; i < sc->sc_nctls; i++) {
+ mc = &sc->sc_ctls[i];
+ if (mc->ctl == SOUND_MIXER_NRDEVICES &&
+ mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) {
+ if (rec_selector_id == -1) {
+ rec_selector_id = i;
+ } else {
+ printf("There are many selectors. Can't recognize which selector is a record source selector.\n");
+ return mask;
+ }
+ }
+ }
+ if (rec_selector_id == -1)
+ return mask;
+ mc = &sc->sc_ctls[rec_selector_id];
+ for (i = mc->minval; i <= mc->maxval; i++) {
+ if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES)
+ continue;
+ mask |= 1 << mc->slctrtype[i - 1];
+ }
+ return mask;
+}
+
+void
+uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right)
+{
+ int i;
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+
+ sc = device_get_softc(dev);
+ for (i=0; i < sc->sc_nctls; i++) {
+ mc = &sc->sc_ctls[i];
+ if (mc->ctl == type) {
+ if (mc->nchan == 2) {
+ /* set Right */
+ uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*255)/100);
+ }
+ /* set Left or Mono */
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*255)/100);
+ }
+ }
+ return;
+}
+
+u_int32_t
+uaudio_mixer_setrecsrc(device_t dev, u_int32_t src)
+{
+ int i, rec_selector_id;
+ struct uaudio_softc *sc;
+ struct mixerctl *mc;
+
+ sc = device_get_softc(dev);
+ rec_selector_id = -1;
+ for (i=0; i < sc->sc_nctls; i++) {
+ mc = &sc->sc_ctls[i];
+ if (mc->ctl == SOUND_MIXER_NRDEVICES &&
+ mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) {
+ if (rec_selector_id == -1) {
+ rec_selector_id = i;
+ } else {
+ return src; /* Can't recognize which selector is record source selector */
+ }
+ }
+ }
+ if (rec_selector_id == -1)
+ return src;
+ mc = &sc->sc_ctls[rec_selector_id];
+ for (i = mc->minval; i <= mc->maxval; i++) {
+ if (src != (1 << mc->slctrtype[i - 1]))
+ continue;
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, i);
+ return (1 << mc->slctrtype[i - 1]);
+ }
+ uaudio_ctl_set(sc, SET_CUR, mc, 0, mc->minval);
+ return (1 << mc->slctrtype[mc->minval - 1]);
+}
+
+static int
+uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
+{
+ struct snddev_info *d;
+ struct pcm_channel *c;
+ struct pcm_feeder *f;
+ device_t pa_dev = device_get_parent(dev);
+ struct uaudio_softc *sc = device_get_softc(pa_dev);
+
+ if (verbose < 1)
+ return 0;
+
+ d = device_get_softc(dev);
+ if (!d)
+ return ENXIO;
+
+ PCM_BUSYASSERT(d);
+
+ if (CHN_EMPTY(d, channels.pcm)) {
+ sbuf_printf(s, " (mixer only)");
+ return 0;
+ }
+
+ sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)",
+ d->playcount, d->pvchancount,
+ d->reccount, d->rvchancount,
+ (d->flags & SD_F_SIMPLEX)? "" : " duplex",
+#ifdef USING_DEVFS
+ (device_get_unit(dev) == snd_unit)? " default" : ""
+#else
+ ""
+#endif
+ );
+
+ if (sc->uaudio_sndstat_flag != 0) {
+ sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat)));
+ }
+
+ if (verbose <= 1)
+ return 0;
+
+ CHN_FOREACH(c, d, channels.pcm) {
+
+ KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
+ ("hosed pcm channel setup"));
+
+ sbuf_printf(s, "\n\t");
+
+ /* it would be better to indent child channels */
+ sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
+ sbuf_printf(s, "spd %d", c->speed);
+ if (c->speed != sndbuf_getspd(c->bufhard))
+ sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
+ sbuf_printf(s, ", fmt 0x%08x", c->format);
+ if (c->format != sndbuf_getfmt(c->bufhard))
+ sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
+ sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
+ if (c->pid != -1)
+ sbuf_printf(s, ", pid %d", c->pid);
+ sbuf_printf(s, "\n\t");
+
+ sbuf_printf(s, "interrupts %d, ", c->interrupts);
+ if (c->direction == PCMDIR_REC)
+ sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
+ sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
+ sndbuf_getblkcnt(c->bufhard),
+ sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
+ sndbuf_getblkcnt(c->bufsoft));
+ else
+ sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
+ c->xruns, c->feedcount, sndbuf_getready(c->bufsoft),
+ sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
+ sndbuf_getblkcnt(c->bufhard),
+ sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
+ sndbuf_getblkcnt(c->bufsoft));
+ sbuf_printf(s, "\n\t");
+
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
+ sbuf_printf(s, " -> ");
+ f = c->feeder;
+ while (f->source != NULL)
+ f = f->source;
+ while (f != NULL) {
+ sbuf_printf(s, "%s", f->class->name);
+ if (f->desc->type == FEEDER_FMT)
+ sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
+ if (f->desc->type == FEEDER_RATE)
+ sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
+ if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
+ f->desc->type == FEEDER_VOLUME)
+ sbuf_printf(s, "(0x%08x)", f->desc->out);
+ sbuf_printf(s, " -> ");
+ f = f->parent;
+ }
+ sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
+ }
+
+ return 0;
+}
+
+void
+uaudio_sndstat_register(device_t dev)
+{
+ struct snddev_info *d = device_get_softc(dev);
+ sndstat_register(dev, d->status, uaudio_sndstat_prepare_pcm);
+}
+
+int
+uaudio_get_vendor(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+
+ if (sc == NULL)
+ return 0;
+
+ return sc->sc_vendor;
+}
+
+int
+uaudio_get_product(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+
+ if (sc == NULL)
+ return 0;
+
+ return sc->sc_product;
+}
+
+int
+uaudio_get_release(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+
+ if (sc == NULL)
+ return 0;
+
+ return sc->sc_release;
+}
+
+static int
+audio_attach_mi(device_t dev)
+{
+ device_t child;
+ struct sndcard_func *func;
+
+ /* Attach the children. */
+ /* PCM Audio */
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (func == NULL)
+ return (ENOMEM);
+ func->func = SCF_PCM;
+ child = device_add_child(dev, "pcm", -1);
+ device_set_ivars(child, func);
+
+ bus_generic_attach(dev);
+
+ return 0; /* XXXXX */
+}
+
+DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, usbd_driver_load, 0);
+MODULE_VERSION(uaudio, 1);
+
+#endif
diff --git a/sys/legacy/dev/sound/usb/uaudio.h b/sys/legacy/dev/sound/usb/uaudio.h
new file mode 100644
index 0000000..b73f7a5
--- /dev/null
+++ b/sys/legacy/dev/sound/usb/uaudio.h
@@ -0,0 +1,56 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#define NO_RECORDING /* XXX: some routines missing from uaudio.c */
+#endif
+
+/* Defined in uaudio.c, used in uaudio_pcm,c */
+
+void uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start,
+ u_char *end, struct pcm_channel *pc, int dir);
+int uaudio_trigger_output(device_t dev);
+int uaudio_halt_out_dma(device_t dev);
+#ifndef NO_RECORDING
+int uaudio_trigger_input(device_t dev);
+int uaudio_halt_in_dma(device_t dev);
+#endif
+void uaudio_chan_set_param(device_t, u_char *, u_char *);
+void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir);
+int uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir);
+void uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir);
+int uaudio_chan_getptr(device_t dev, int);
+void uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
+ unsigned right);
+u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src);
+u_int32_t uaudio_query_mix_info(device_t dev);
+u_int32_t uaudio_query_recsrc_info(device_t dev);
+unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt);
+void uaudio_sndstat_register(device_t dev);
+int uaudio_get_vendor(device_t dev);
+int uaudio_get_product(device_t dev);
+int uaudio_get_release(device_t dev);
diff --git a/sys/legacy/dev/sound/usb/uaudio_pcm.c b/sys/legacy/dev/sound/usb/uaudio_pcm.c
new file mode 100644
index 0000000..618a0ed
--- /dev/null
+++ b/sys/legacy/dev/sound/usb/uaudio_pcm.c
@@ -0,0 +1,481 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/soundcard.h>
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include <dev/sound/usb/uaudio.h>
+
+#include "mixer_if.h"
+
+struct ua_info;
+
+struct ua_chinfo {
+ struct ua_info *parent;
+ struct pcm_channel *channel;
+ struct snd_dbuf *buffer;
+ u_char *buf;
+ int dir, hwch;
+ u_int32_t fmt, spd, blksz; /* XXXXX */
+};
+
+struct ua_info {
+ device_t sc_dev;
+ u_int32_t bufsz;
+ struct ua_chinfo pch, rch;
+#define FORMAT_NUM 32
+ u_int32_t ua_playfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+ u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */
+ struct pcmchan_caps ua_playcaps;
+ struct pcmchan_caps ua_reccaps;
+ int vendor, product, release;
+};
+
+#define UAUDIO_DEFAULT_BUFSZ 16*1024
+
+static const struct {
+ int vendor;
+ int product;
+ int release;
+ uint32_t dflags;
+} ua_quirks[] = {
+ { 0x1130, 0xf211, 0x0101, SD_F_PSWAPLR },
+};
+
+/************************************************************/
+static void *
+ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ device_t pa_dev;
+
+ struct ua_info *sc = devinfo;
+ struct ua_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch;
+
+ ch->parent = sc;
+ ch->channel = c;
+ ch->buffer = b;
+ ch->dir = dir;
+
+ pa_dev = device_get_parent(sc->sc_dev);
+
+ ch->buf = malloc(sc->bufsz, M_DEVBUF, M_NOWAIT);
+ if (ch->buf == NULL)
+ return NULL;
+ if (sndbuf_setup(b, ch->buf, sc->bufsz) != 0) {
+ free(ch->buf, M_DEVBUF);
+ return NULL;
+ }
+ uaudio_chan_set_param_pcm_dma_buff(pa_dev, ch->buf, ch->buf+sc->bufsz, ch->channel, dir);
+ if (bootverbose)
+ device_printf(pa_dev, "%s buf %p\n", (dir == PCMDIR_PLAY)?
+ "play" : "rec", sndbuf_getbuf(ch->buffer));
+
+ ch->dir = dir;
+#ifndef NO_RECORDING
+ ch->hwch = 1;
+ if (dir == PCMDIR_PLAY)
+ ch->hwch = 2;
+#else
+ ch->hwch = 2;
+#endif
+
+ return ch;
+}
+
+static int
+ua_chan_free(kobj_t obj, void *data)
+{
+ struct ua_chinfo *ua = data;
+
+ if (ua->buf != NULL)
+ free(ua->buf, M_DEVBUF);
+ return 0;
+}
+
+static int
+ua_chan_setformat(kobj_t obj, void *data, u_int32_t format)
+{
+ device_t pa_dev;
+ struct ua_info *ua;
+
+ struct ua_chinfo *ch = data;
+
+ /*
+ * At this point, no need to query as we shouldn't select an unsorted format
+ */
+ ua = ch->parent;
+ pa_dev = device_get_parent(ua->sc_dev);
+ uaudio_chan_set_param_format(pa_dev, format, ch->dir);
+
+ ch->fmt = format;
+ return 0;
+}
+
+static int
+ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
+{
+ struct ua_chinfo *ch;
+ device_t pa_dev;
+ int bestspeed;
+
+ ch = data;
+ pa_dev = device_get_parent(ch->parent->sc_dev);
+
+ if ((bestspeed = uaudio_chan_set_param_speed(pa_dev, speed, ch->dir)))
+ ch->spd = bestspeed;
+
+ return ch->spd;
+}
+
+static int
+ua_chan_setfragments(kobj_t obj, void *data, u_int32_t blksz, u_int32_t blkcnt)
+{
+ device_t pa_dev;
+ struct ua_chinfo *ch = data;
+ struct ua_info *ua = ch->parent;
+
+ RANGE(blksz, 128, sndbuf_getmaxsize(ch->buffer) / 2);
+ RANGE(blkcnt, 2, 512);
+
+ while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->buffer)) {
+ if ((blkcnt >> 1) >= 2)
+ blkcnt >>= 1;
+ else if ((blksz >> 1) >= 128)
+ blksz >>= 1;
+ else
+ break;
+ }
+
+ if ((sndbuf_getblksz(ch->buffer) != blksz ||
+ sndbuf_getblkcnt(ch->buffer) != blkcnt) &&
+ sndbuf_resize(ch->buffer, blkcnt, blksz) != 0)
+ device_printf(ua->sc_dev, "%s: failed blksz=%u blkcnt=%u\n",
+ __func__, blksz, blkcnt);
+
+ ch->blksz = sndbuf_getblksz(ch->buffer);
+
+ pa_dev = device_get_parent(ua->sc_dev);
+ uaudio_chan_set_param_pcm_dma_buff(pa_dev, ch->buf,
+ ch->buf + sndbuf_getsize(ch->buffer), ch->channel, ch->dir);
+ uaudio_chan_set_param_blocksize(pa_dev, ch->blksz, ch->dir);
+
+ return 1;
+}
+
+static int
+ua_chan_setblocksize(kobj_t obj, void *data, u_int32_t blksz)
+{
+ struct ua_chinfo *ch = data;
+
+ ua_chan_setfragments(obj, data, blksz,
+ sndbuf_getmaxsize(ch->buffer) / blksz);
+
+ return ch->blksz;
+}
+
+static int
+ua_chan_trigger(kobj_t obj, void *data, int go)
+{
+ device_t pa_dev;
+ struct ua_info *ua;
+ struct ua_chinfo *ch = data;
+
+ if (!PCMTRIG_COMMON(go))
+ return 0;
+
+ ua = ch->parent;
+ pa_dev = device_get_parent(ua->sc_dev);
+
+ /* XXXXX */
+ if (ch->dir == PCMDIR_PLAY) {
+ if (go == PCMTRIG_START) {
+ uaudio_trigger_output(pa_dev);
+ } else {
+ uaudio_halt_out_dma(pa_dev);
+ }
+ } else {
+#ifndef NO_RECORDING
+ if (go == PCMTRIG_START)
+ uaudio_trigger_input(pa_dev);
+ else
+ uaudio_halt_in_dma(pa_dev);
+#endif
+ }
+
+ return 0;
+}
+
+static int
+ua_chan_getptr(kobj_t obj, void *data)
+{
+ device_t pa_dev;
+ struct ua_info *ua;
+ struct ua_chinfo *ch = data;
+
+ ua = ch->parent;
+ pa_dev = device_get_parent(ua->sc_dev);
+
+ return uaudio_chan_getptr(pa_dev, ch->dir);
+}
+
+static struct pcmchan_caps *
+ua_chan_getcaps(kobj_t obj, void *data)
+{
+ struct ua_chinfo *ch;
+
+ ch = data;
+ return (ch->dir == PCMDIR_PLAY) ? &(ch->parent->ua_playcaps) : &(ch->parent->ua_reccaps);
+}
+
+static kobj_method_t ua_chan_methods[] = {
+ KOBJMETHOD(channel_init, ua_chan_init),
+ KOBJMETHOD(channel_free, ua_chan_free),
+ KOBJMETHOD(channel_setformat, ua_chan_setformat),
+ KOBJMETHOD(channel_setspeed, ua_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize),
+ KOBJMETHOD(channel_setfragments, ua_chan_setfragments),
+ KOBJMETHOD(channel_trigger, ua_chan_trigger),
+ KOBJMETHOD(channel_getptr, ua_chan_getptr),
+ KOBJMETHOD(channel_getcaps, ua_chan_getcaps),
+ { 0, 0 }
+};
+
+CHANNEL_DECLARE(ua_chan);
+
+/************************************************************/
+static int
+ua_mixer_init(struct snd_mixer *m)
+{
+ u_int32_t mask;
+ device_t pa_dev;
+ struct ua_info *ua = mix_getdevinfo(m);
+
+ pa_dev = device_get_parent(ua->sc_dev);
+
+ mask = uaudio_query_mix_info(pa_dev);
+ if (!(mask & SOUND_MASK_PCM)) {
+ /*
+ * Emulate missing pcm mixer controller
+ * through FEEDER_VOLUME
+ */
+ pcm_setflags(ua->sc_dev, pcm_getflags(ua->sc_dev) |
+ SD_F_SOFTPCMVOL);
+ }
+ if (!(mask & SOUND_MASK_VOLUME)) {
+ mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
+ mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
+ }
+ mix_setdevs(m, mask);
+
+ mask = uaudio_query_recsrc_info(pa_dev);
+ mix_setrecdevs(m, mask);
+
+ return 0;
+}
+
+static int
+ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right)
+{
+ device_t pa_dev;
+ struct ua_info *ua = mix_getdevinfo(m);
+
+ pa_dev = device_get_parent(ua->sc_dev);
+ uaudio_mixer_set(pa_dev, type, left, right);
+
+ return left | (right << 8);
+}
+
+static int
+ua_mixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
+{
+ device_t pa_dev;
+ struct ua_info *ua = mix_getdevinfo(m);
+
+ pa_dev = device_get_parent(ua->sc_dev);
+ return uaudio_mixer_setrecsrc(pa_dev, src);
+}
+
+static kobj_method_t ua_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, ua_mixer_init),
+ KOBJMETHOD(mixer_set, ua_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc),
+
+ { 0, 0 }
+};
+MIXER_DECLARE(ua_mixer);
+/************************************************************/
+
+
+static int
+ua_probe(device_t dev)
+{
+ char *s;
+ struct sndcard_func *func;
+
+ /* The parent device has already been probed. */
+
+ func = device_get_ivars(dev);
+ if (func == NULL || func->func != SCF_PCM)
+ return (ENXIO);
+
+ s = "USB Audio";
+
+ device_set_desc(dev, s);
+ return BUS_PROBE_DEFAULT;
+}
+
+static int
+ua_attach(device_t dev)
+{
+ struct ua_info *ua;
+ struct sndcard_func *func;
+ char status[SND_STATUSLEN];
+ device_t pa_dev;
+ u_int32_t nplay, nrec, flags;
+ int i;
+
+ ua = malloc(sizeof(*ua), M_DEVBUF, M_WAITOK | M_ZERO);
+ ua->sc_dev = dev;
+
+ /* Mark for existence */
+ func = device_get_ivars(dev);
+ if (func != NULL)
+ func->varinfo = (void *)ua;
+
+ pa_dev = device_get_parent(dev);
+ ua->vendor = uaudio_get_vendor(pa_dev);
+ ua->product = uaudio_get_product(pa_dev);
+ ua->release = uaudio_get_release(pa_dev);
+
+ if (bootverbose)
+ device_printf(dev,
+ "USB Audio: "
+ "vendor=0x%04x, product=0x%04x, release=0x%04x\n",
+ ua->vendor, ua->product, ua->release);
+
+ ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536);
+ if (bootverbose)
+ device_printf(dev, "using a default buffer size of %jd\n", (intmax_t)ua->bufsz);
+
+ if (mixer_init(dev, &ua_mixer_class, ua)) {
+ goto bad;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at ? %s", PCM_KLDSTRING(snd_uaudio));
+
+ ua->ua_playcaps.fmtlist = ua->ua_playfmt;
+ ua->ua_reccaps.fmtlist = ua->ua_recfmt;
+ nplay = uaudio_query_formats(pa_dev, PCMDIR_PLAY, FORMAT_NUM * 2, &ua->ua_playcaps);
+ nrec = uaudio_query_formats(pa_dev, PCMDIR_REC, FORMAT_NUM * 2, &ua->ua_reccaps);
+
+ if (nplay > 1)
+ nplay = 1;
+ if (nrec > 1)
+ nrec = 1;
+
+ flags = pcm_getflags(dev);
+ for (i = 0; i < (sizeof(ua_quirks) / sizeof(ua_quirks[0])); i++) {
+ if (ua->vendor == ua_quirks[i].vendor &&
+ ua->product == ua_quirks[i].product &&
+ ua->release == ua_quirks[i].release)
+ flags |= ua_quirks[i].dflags;
+ }
+ pcm_setflags(dev, flags);
+
+#ifndef NO_RECORDING
+ if (pcm_register(dev, ua, nplay, nrec)) {
+#else
+ if (pcm_register(dev, ua, nplay, 0)) {
+#endif
+ goto bad;
+ }
+
+ sndstat_unregister(dev);
+ uaudio_sndstat_register(dev);
+
+ for (i = 0; i < nplay; i++) {
+ pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua);
+ }
+#ifndef NO_RECORDING
+ for (i = 0; i < nrec; i++) {
+ pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua);
+ }
+#endif
+ pcm_setstatus(dev, status);
+
+ return 0;
+
+bad: free(ua, M_DEVBUF);
+ return ENXIO;
+}
+
+static int
+ua_detach(device_t dev)
+{
+ struct ua_info *sc;
+ struct sndcard_func *func;
+ int r;
+
+ r = pcm_unregister(dev);
+ if (r)
+ return r;
+
+ sc = pcm_getdevinfo(dev);
+ free(sc, M_DEVBUF);
+
+ /* Mark for deletion */
+ func = device_get_ivars(dev);
+ if (func != NULL)
+ func->varinfo = NULL;
+
+ return 0;
+}
+
+/************************************************************/
+
+static device_method_t ua_pcm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ua_probe),
+ DEVMETHOD(device_attach, ua_attach),
+ DEVMETHOD(device_detach, ua_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ua_pcm_driver = {
+ "pcm",
+ ua_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+
+DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1);
+MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(ua_pcm, 1);
diff --git a/sys/legacy/dev/sound/usb/uaudioreg.h b/sys/legacy/dev/sound/usb/uaudioreg.h
new file mode 100644
index 0000000..3cc09ba
--- /dev/null
+++ b/sys/legacy/dev/sound/usb/uaudioreg.h
@@ -0,0 +1,404 @@
+/* $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define UAUDIO_VERSION 0x100
+
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+
+#define UDESCSUB_AC_HEADER 1
+#define UDESCSUB_AC_INPUT 2
+#define UDESCSUB_AC_OUTPUT 3
+#define UDESCSUB_AC_MIXER 4
+#define UDESCSUB_AC_SELECTOR 5
+#define UDESCSUB_AC_FEATURE 6
+#define UDESCSUB_AC_PROCESSING 7
+#define UDESCSUB_AC_EXTENSION 8
+
+/* The first fields are identical to usb_endpoint_descriptor_t */
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+ uByte bmAttributes;
+ uWord wMaxPacketSize;
+ uByte bInterval;
+ /*
+ * The following two entries are only used by the Audio Class.
+ * And according to the specs the Audio Class is the only one
+ * allowed to extend the endpoint descriptor.
+ * Who knows what goes on in the minds of the people in the USB
+ * standardization? :-(
+ */
+ uByte bRefresh;
+ uByte bSynchAddress;
+} UPACKED usb_endpoint_descriptor_audio_t;
+
+struct usb_audio_control_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uWord bcdADC;
+ uWord wTotalLength;
+ uByte bInCollection;
+ uByte baInterfaceNr[1];
+} UPACKED;
+
+struct usb_audio_streaming_interface_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalLink;
+ uByte bDelay;
+ uWord wFormatTag;
+} UPACKED;
+
+struct usb_audio_streaming_endpoint_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmAttributes;
+#define UA_SED_FREQ_CONTROL 0x01
+#define UA_SED_PITCH_CONTROL 0x02
+#define UA_SED_MAXPACKETSONLY 0x80
+ uByte bLockDelayUnits;
+ uWord wLockDelay;
+} UPACKED;
+
+struct usb_audio_streaming_type1_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bFormatType;
+ uByte bNrChannels;
+ uByte bSubFrameSize;
+ uByte bBitResolution;
+ uByte bSamFreqType;
+#define UA_SAMP_CONTNUOUS 0
+ uByte tSamFreq[3*2]; /* room for low and high */
+#define UA_GETSAMP(p, n) ((p)->tSamFreq[(n)*3+0] | ((p)->tSamFreq[(n)*3+1] << 8) | ((p)->tSamFreq[(n)*3+2] << 16))
+#define UA_SAMP_LO(p) UA_GETSAMP(p, 0)
+#define UA_SAMP_HI(p) UA_GETSAMP(p, 1)
+} UPACKED;
+
+struct usb_audio_cluster {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+#define UA_CHANNEL_LEFT 0x0001
+#define UA_CHANNEL_RIGHT 0x0002
+#define UA_CHANNEL_CENTER 0x0004
+#define UA_CHANNEL_LFE 0x0008
+#define UA_CHANNEL_L_SURROUND 0x0010
+#define UA_CHANNEL_R_SURROUND 0x0020
+#define UA_CHANNEL_L_CENTER 0x0040
+#define UA_CHANNEL_R_CENTER 0x0080
+#define UA_CHANNEL_SURROUND 0x0100
+#define UA_CHANNEL_L_SIDE 0x0200
+#define UA_CHANNEL_R_SIDE 0x0400
+#define UA_CHANNEL_TOP 0x0800
+ uByte iChannelNames;
+} UPACKED;
+
+/* Shared by all units and terminals */
+struct usb_audio_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+};
+
+/* UDESCSUB_AC_INPUT */
+struct usb_audio_input_terminal {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalId;
+ uWord wTerminalType;
+ uByte bAssocTerminal;
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte iTerminal;
+} UPACKED;
+
+/* UDESCSUB_AC_OUTPUT */
+struct usb_audio_output_terminal {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalId;
+ uWord wTerminalType;
+ uByte bAssocTerminal;
+ uByte bSourceId;
+ uByte iTerminal;
+} UPACKED;
+
+/* UDESCSUB_AC_MIXER */
+struct usb_audio_mixer_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bNrInPins;
+ uByte baSourceId[255]; /* [bNrInPins] */
+ /* struct usb_audio_mixer_unit_1 */
+} UPACKED;
+struct usb_audio_mixer_unit_1 {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bmControls[255]; /* [bNrChannels] */
+ /*uByte iMixer;*/
+} UPACKED;
+
+/* UDESCSUB_AC_SELECTOR */
+struct usb_audio_selector_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bNrInPins;
+ uByte baSourceId[255]; /* [bNrInPins] */
+ /* uByte iSelector; */
+} UPACKED;
+
+/* UDESCSUB_AC_FEATURE */
+struct usb_audio_feature_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bSourceId;
+ uByte bControlSize;
+ uByte bmaControls[255]; /* size for more than enough */
+ /* uByte iFeature; */
+} UPACKED;
+
+/* UDESCSUB_AC_PROCESSING */
+struct usb_audio_processing_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uWord wProcessType;
+ uByte bNrInPins;
+ uByte baSourceId[255]; /* [bNrInPins] */
+ /* struct usb_audio_processing_unit_1 */
+} UPACKED;
+struct usb_audio_processing_unit_1{
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bControlSize;
+ uByte bmControls[255]; /* [bControlSize] */
+#define UA_PROC_ENABLE_MASK 1
+} UPACKED;
+
+struct usb_audio_processing_unit_updown {
+ uByte iProcessing;
+ uByte bNrModes;
+ uWord waModes[255]; /* [bNrModes] */
+} UPACKED;
+
+/* UDESCSUB_AC_EXTENSION */
+struct usb_audio_extension_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uWord wExtensionCode;
+ uByte bNrInPins;
+ uByte baSourceId[255]; /* [bNrInPins] */
+ /* struct usb_audio_extension_unit_1 */
+} UPACKED;
+struct usb_audio_extension_unit_1 {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bControlSize;
+ uByte bmControls[255]; /* [bControlSize] */
+#define UA_EXT_ENABLE_MASK 1
+#define UA_EXT_ENABLE 1
+ /*uByte iExtension;*/
+} UPACKED;
+
+/* USB terminal types */
+#define UAT_UNDEFINED 0x0100
+#define UAT_STREAM 0x0101
+#define UAT_VENDOR 0x01ff
+/* input terminal types */
+#define UATI_UNDEFINED 0x0200
+#define UATI_MICROPHONE 0x0201
+#define UATI_DESKMICROPHONE 0x0202
+#define UATI_PERSONALMICROPHONE 0x0203
+#define UATI_OMNIMICROPHONE 0x0204
+#define UATI_MICROPHONEARRAY 0x0205
+#define UATI_PROCMICROPHONEARR 0x0206
+/* output terminal types */
+#define UATO_UNDEFINED 0x0300
+#define UATO_SPEAKER 0x0301
+#define UATO_HEADPHONES 0x0302
+#define UATO_DISPLAYAUDIO 0x0303
+#define UATO_DESKTOPSPEAKER 0x0304
+#define UATO_ROOMSPEAKER 0x0305
+#define UATO_COMMSPEAKER 0x0306
+#define UATO_SUBWOOFER 0x0307
+/* bidir terminal types */
+#define UATB_UNDEFINED 0x0400
+#define UATB_HANDSET 0x0401
+#define UATB_HEADSET 0x0402
+#define UATB_SPEAKERPHONE 0x0403
+#define UATB_SPEAKERPHONEESUP 0x0404
+#define UATB_SPEAKERPHONEECANC 0x0405
+/* telephony terminal types */
+#define UATT_UNDEFINED 0x0500
+#define UATT_PHONELINE 0x0501
+#define UATT_TELEPHONE 0x0502
+#define UATT_DOWNLINEPHONE 0x0503
+/* external terminal types */
+#define UATE_UNDEFINED 0x0600
+#define UATE_ANALOGCONN 0x0601
+#define UATE_DIGITALAUIFC 0x0602
+#define UATE_LINECONN 0x0603
+#define UATE_LEGACYCONN 0x0604
+#define UATE_SPDIF 0x0605
+#define UATE_1394DA 0x0606
+#define UATE_1394DV 0x0607
+/* embedded function terminal types */
+#define UATF_UNDEFINED 0x0700
+#define UATF_CALIBNOISE 0x0701
+#define UATF_EQUNOISE 0x0702
+#define UATF_CDPLAYER 0x0703
+#define UATF_DAT 0x0704
+#define UATF_DCC 0x0705
+#define UATF_MINIDISK 0x0706
+#define UATF_ANALOGTAPE 0x0707
+#define UATF_PHONOGRAPH 0x0708
+#define UATF_VCRAUDIO 0x0709
+#define UATF_VIDEODISCAUDIO 0x070a
+#define UATF_DVDAUDIO 0x070b
+#define UATF_TVTUNERAUDIO 0x070c
+#define UATF_SATELLITE 0x070d
+#define UATF_CABLETUNER 0x070e
+#define UATF_DSS 0x070f
+#define UATF_RADIORECV 0x0710
+#define UATF_RADIOXMIT 0x0711
+#define UATF_MULTITRACK 0x0712
+#define UATF_SYNTHESIZER 0x0713
+
+
+#define SET_CUR 0x01
+#define GET_CUR 0x81
+#define SET_MIN 0x02
+#define GET_MIN 0x82
+#define SET_MAX 0x03
+#define GET_MAX 0x83
+#define SET_RES 0x04
+#define GET_RES 0x84
+#define SET_MEM 0x05
+#define GET_MEM 0x85
+#define GET_STAT 0xff
+
+#define MUTE_CONTROL 0x01
+#define VOLUME_CONTROL 0x02
+#define BASS_CONTROL 0x03
+#define MID_CONTROL 0x04
+#define TREBLE_CONTROL 0x05
+#define GRAPHIC_EQUALIZER_CONTROL 0x06
+#define AGC_CONTROL 0x07
+#define DELAY_CONTROL 0x08
+#define BASS_BOOST_CONTROL 0x09
+#define LOUDNESS_CONTROL 0x0a
+
+#define FU_MASK(u) (1 << ((u)-1))
+
+#define MASTER_CHAN 0
+
+#define AS_GENERAL 1
+#define FORMAT_TYPE 2
+#define FORMAT_SPECIFIC 3
+
+#define UA_FMT_PCM 1
+#define UA_FMT_PCM8 2
+#define UA_FMT_IEEE_FLOAT 3
+#define UA_FMT_ALAW 4
+#define UA_FMT_MULAW 5
+#define UA_FMT_MPEG 0x1001
+#define UA_FMT_AC3 0x1002
+
+#define SAMPLING_FREQ_CONTROL 0x01
+#define PITCH_CONTROL 0x02
+
+#define FORMAT_TYPE_UNDEFINED 0
+#define FORMAT_TYPE_I 1
+#define FORMAT_TYPE_II 2
+#define FORMAT_TYPE_III 3
+
+#define UA_PROC_MASK(n) (1<< ((n)-1))
+#define PROCESS_UNDEFINED 0
+#define XX_ENABLE_CONTROL 1
+#define UPDOWNMIX_PROCESS 1
+#define UD_ENABLE_CONTROL 1
+#define UD_MODE_SELECT_CONTROL 2
+#define DOLBY_PROLOGIC_PROCESS 2
+#define DP_ENABLE_CONTROL 1
+#define DP_MODE_SELECT_CONTROL 2
+#define P3D_STEREO_EXTENDER_PROCESS 3
+#define P3D_ENABLE_CONTROL 1
+#define P3D_SPACIOUSNESS_CONTROL 2
+#define REVERBATION_PROCESS 4
+#define RV_ENABLE_CONTROL 1
+#define RV_LEVEL_CONTROL 2
+#define RV_TIME_CONTROL 3
+#define RV_FEEDBACK_CONTROL 4
+#define CHORUS_PROCESS 5
+#define CH_ENABLE_CONTROL 1
+#define CH_LEVEL_CONTROL 2
+#define CH_RATE_CONTROL 3
+#define CH_DEPTH_CONTROL 4
+#define DYN_RANGE_COMP_PROCESS 6
+#define DR_ENABLE_CONTROL 1
+#define DR_COMPRESSION_RATE_CONTROL 2
+#define DR_MAXAMPL_CONTROL 3
+#define DR_THRESHOLD_CONTROL 4
+#define DR_ATTACK_TIME_CONTROL 5
+#define DR_RELEASE_TIME_CONTROL 6
OpenPOWER on IntegriCloud