2012-05-18 11:52:54 +04:00
|
|
|
/* $NetBSD: uaudio.c,v 1.132 2012/05/18 07:52:54 jdc Exp $ */
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1999 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
1999-10-25 14:16:49 +04:00
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
2000-04-27 19:26:44 +04:00
|
|
|
* by Lennart Augustsson (lennart@augustsson.net) at
|
1999-10-25 14:16:49 +04:00
|
|
|
* Carlstedt Research & Technology.
|
1999-09-09 16:28:25 +04:00
|
|
|
*
|
|
|
|
* 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 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2003-09-29 15:54:26 +04:00
|
|
|
* 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
|
1999-09-09 16:28:25 +04:00
|
|
|
*/
|
|
|
|
|
2001-11-13 09:24:53 +03:00
|
|
|
#include <sys/cdefs.h>
|
2012-05-18 11:52:54 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.132 2012/05/18 07:52:54 jdc Exp $");
|
2001-11-13 09:24:53 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/file.h>
|
2001-01-21 03:07:13 +03:00
|
|
|
#include <sys/reboot.h> /* for bootverbose */
|
1999-09-09 16:28:25 +04:00
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/poll.h>
|
2008-06-28 13:14:56 +04:00
|
|
|
#include <sys/module.h>
|
2010-12-24 23:49:55 +03:00
|
|
|
#include <sys/bus.h>
|
2011-11-27 08:10:38 +04:00
|
|
|
#include <sys/cpu.h>
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
#include <sys/audioio.h>
|
|
|
|
#include <dev/audio_if.h>
|
2004-07-17 02:39:23 +04:00
|
|
|
#include <dev/audiovar.h>
|
1999-09-09 16:28:25 +04:00
|
|
|
#include <dev/mulaw.h>
|
|
|
|
#include <dev/auconv.h>
|
|
|
|
|
|
|
|
#include <dev/usb/usb.h>
|
|
|
|
#include <dev/usb/usbdi.h>
|
2010-12-24 23:49:55 +03:00
|
|
|
#include <dev/usb/usbdivar.h>
|
1999-09-09 16:28:25 +04:00
|
|
|
#include <dev/usb/usbdi_util.h>
|
|
|
|
#include <dev/usb/usb_quirks.h>
|
|
|
|
|
2010-12-28 23:11:18 +03:00
|
|
|
#include <dev/usb/usbdevs.h>
|
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
#include <dev/usb/uaudioreg.h>
|
|
|
|
|
2004-10-16 22:08:50 +04:00
|
|
|
/* #define UAUDIO_DEBUG */
|
|
|
|
/* #define UAUDIO_MULTIPLE_ENDPOINTS */
|
1999-10-14 05:18:39 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2011-11-27 08:10:38 +04:00
|
|
|
#define DPRINTF(x,y...) do { \
|
|
|
|
if (uaudiodebug) { \
|
|
|
|
struct lwp *l = curlwp; \
|
|
|
|
printf("%s[%d:%d]: "x, __func__, l->l_proc->p_pid, l->l_lid, y); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
#define DPRINTFN_CLEAN(n,x...) do { \
|
|
|
|
if (uaudiodebug > (n)) \
|
|
|
|
printf(x); \
|
|
|
|
} while (0)
|
|
|
|
#define DPRINTFN(n,x,y...) do { \
|
|
|
|
if (uaudiodebug > (n)) { \
|
|
|
|
struct lwp *l = curlwp; \
|
|
|
|
printf("%s[%d:%d]: "x, __func__, l->l_proc->p_pid, l->l_lid, y); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2011-11-27 11:36:54 +04:00
|
|
|
int uaudiodebug = 6;
|
1999-09-09 16:28:25 +04:00
|
|
|
#else
|
2011-11-27 08:10:38 +04:00
|
|
|
#define DPRINTF(x,y...)
|
|
|
|
#define DPRINTFN_CLEAN(n,x...)
|
|
|
|
#define DPRINTFN(n,x,y...)
|
1999-09-09 16:28:25 +04:00
|
|
|
#endif
|
|
|
|
|
2000-03-29 05:47:25 +04:00
|
|
|
#define UAUDIO_NCHANBUFS 6 /* number of outstanding request */
|
2002-03-23 19:23:20 +03:00
|
|
|
#define UAUDIO_NFRAMES 10 /* ms of sound in each request */
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
|
|
|
|
#define MIX_MAX_CHAN 8
|
|
|
|
struct mixerctl {
|
2005-01-15 18:19:51 +03:00
|
|
|
uint16_t wValue[MIX_MAX_CHAN]; /* using nchan */
|
|
|
|
uint16_t wIndex;
|
|
|
|
uint8_t nchan;
|
|
|
|
uint8_t type;
|
1999-09-09 16:28:25 +04:00
|
|
|
#define MIX_ON_OFF 1
|
|
|
|
#define MIX_SIGNED_16 2
|
|
|
|
#define MIX_UNSIGNED_16 3
|
|
|
|
#define MIX_SIGNED_8 4
|
2004-10-03 10:01:09 +04:00
|
|
|
#define MIX_SELECTOR 5
|
1999-09-09 16:28:25 +04:00
|
|
|
#define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1)
|
|
|
|
#define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
|
2000-12-29 17:49:28 +03:00
|
|
|
int minval, maxval;
|
|
|
|
u_int delta;
|
|
|
|
u_int mul;
|
2005-01-15 18:19:51 +03:00
|
|
|
uint8_t class;
|
1999-09-09 16:28:25 +04:00
|
|
|
char ctlname[MAX_AUDIO_DEV_LEN];
|
2005-05-30 08:21:39 +04:00
|
|
|
const char *ctlunit;
|
1999-09-09 16:28:25 +04:00
|
|
|
};
|
|
|
|
#define MAKE(h,l) (((h) << 8) | (l))
|
|
|
|
|
|
|
|
struct as_info {
|
2005-01-15 18:19:51 +03:00
|
|
|
uint8_t alt;
|
|
|
|
uint8_t encoding;
|
|
|
|
uint8_t attributes; /* Copy of bmAttributes of
|
2002-03-07 17:37:02 +03:00
|
|
|
* usb_audio_streaming_endpoint_descriptor
|
|
|
|
*/
|
2001-01-04 09:20:49 +03:00
|
|
|
usbd_interface_handle ifaceh;
|
2004-10-02 23:14:54 +04:00
|
|
|
const usb_interface_descriptor_t *idesc;
|
|
|
|
const usb_endpoint_descriptor_audio_t *edesc;
|
2004-10-16 22:08:50 +04:00
|
|
|
const usb_endpoint_descriptor_audio_t *edesc1;
|
2004-10-02 23:14:54 +04:00
|
|
|
const struct usb_audio_streaming_type1_descriptor *asf1desc;
|
2004-11-13 18:00:48 +03:00
|
|
|
struct audio_format *aformat;
|
2002-02-12 22:52:43 +03:00
|
|
|
int sc_busy; /* currently used */
|
1999-09-09 16:28:25 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct chan {
|
2003-05-03 22:10:37 +04:00
|
|
|
void (*intr)(void *); /* DMA completion intr handler */
|
1999-09-09 16:28:25 +04:00
|
|
|
void *arg; /* arg for intr() */
|
|
|
|
usbd_pipe_handle pipe;
|
2004-10-22 19:25:56 +04:00
|
|
|
usbd_pipe_handle sync_pipe;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
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 */
|
|
|
|
|
2002-02-12 22:52:43 +03:00
|
|
|
int altidx; /* currently used altidx */
|
2000-12-28 14:56:22 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
int curchanbuf;
|
|
|
|
struct chanbuf {
|
2002-03-23 20:17:10 +03:00
|
|
|
struct chan *chan;
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_xfer_handle xfer;
|
2002-03-23 20:17:10 +03:00
|
|
|
u_char *buffer;
|
2005-01-15 18:19:51 +03:00
|
|
|
uint16_t sizes[UAUDIO_NFRAMES];
|
|
|
|
uint16_t offsets[UAUDIO_NFRAMES];
|
|
|
|
uint16_t size;
|
1999-09-09 16:28:25 +04:00
|
|
|
} chanbufs[UAUDIO_NCHANBUFS];
|
1999-11-09 19:52:14 +03:00
|
|
|
|
|
|
|
struct uaudio_softc *sc; /* our softc */
|
1999-09-09 16:28:25 +04:00
|
|
|
};
|
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
/*
|
|
|
|
* XXX Locking notes:
|
|
|
|
*
|
|
|
|
* The MI USB audio subsystem is not MP-SAFE. Our strategy here
|
|
|
|
* is to ensure we have the kernel lock held when calling into
|
|
|
|
* usbd, and, generally, to have dropped the sc_intr_lock during
|
|
|
|
* these sections as well since the usb code will sleep.
|
|
|
|
*/
|
1999-09-09 16:28:25 +04:00
|
|
|
struct uaudio_softc {
|
2010-11-04 01:34:23 +03:00
|
|
|
device_t sc_dev; /* base device */
|
2011-11-24 03:07:28 +04:00
|
|
|
kmutex_t sc_lock;
|
|
|
|
kmutex_t sc_intr_lock;
|
1999-09-09 16:28:25 +04:00
|
|
|
usbd_device_handle sc_udev; /* USB device */
|
2004-10-22 20:01:49 +04:00
|
|
|
int sc_ac_iface; /* Audio Control interface */
|
1999-09-09 16:28:25 +04:00
|
|
|
usbd_interface_handle sc_ac_ifaceh;
|
2004-10-22 20:01:49 +04:00
|
|
|
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;
|
2002-03-23 20:17:10 +03:00
|
|
|
#define HAS_8 0x01
|
|
|
|
#define HAS_16 0x02
|
|
|
|
#define HAS_8U 0x04
|
|
|
|
#define HAS_ALAW 0x08
|
|
|
|
#define HAS_MULAW 0x10
|
2002-02-12 22:52:43 +03:00
|
|
|
#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */
|
2002-03-12 18:12:03 +03:00
|
|
|
#define HAS_24 0x40
|
2004-10-22 20:01:49 +04:00
|
|
|
int sc_mode; /* play/record capability */
|
|
|
|
struct mixerctl *sc_ctls; /* mixer controls */
|
|
|
|
int sc_nctls; /* # of mixer controls */
|
2009-11-12 22:50:01 +03:00
|
|
|
device_t sc_audiodev;
|
2004-11-13 18:00:48 +03:00
|
|
|
struct audio_format *sc_formats;
|
|
|
|
int sc_nformats;
|
|
|
|
struct audio_encoding_set *sc_encodings;
|
|
|
|
u_int sc_channel_config;
|
2004-10-22 20:01:49 +04:00
|
|
|
char sc_dying;
|
2010-12-24 23:49:55 +03:00
|
|
|
struct audio_device sc_adev;
|
1999-09-09 16:28:25 +04:00
|
|
|
};
|
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
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 {
|
2007-02-26 16:14:11 +03:00
|
|
|
const uaudio_cs_descriptor_t *desc;
|
2004-10-21 16:41:07 +04:00
|
|
|
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 */
|
2004-10-22 19:25:56 +04:00
|
|
|
int direct; /* directly connected to an output terminal */
|
2004-10-21 16:41:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#define UAC_OUTPUT 0
|
|
|
|
#define UAC_INPUT 1
|
|
|
|
#define UAC_EQUAL 2
|
|
|
|
#define UAC_RECORD 3
|
|
|
|
#define UAC_NCLASSES 4
|
|
|
|
#ifdef UAUDIO_DEBUG
|
|
|
|
Static const char *uac_names[] = {
|
|
|
|
AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord,
|
|
|
|
};
|
|
|
|
#endif
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
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 *);
|
2004-10-02 23:14:54 +04:00
|
|
|
|
|
|
|
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 *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static char *uaudio_id_name
|
|
|
|
(struct uaudio_softc *, const struct io_terminal *, int);
|
2004-11-05 20:46:14 +03:00
|
|
|
#ifdef UAUDIO_DEBUG
|
|
|
|
Static void uaudio_dump_cluster(const struct usb_audio_cluster *);
|
|
|
|
#endif
|
2004-10-22 20:01:49 +04:00
|
|
|
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);
|
2004-10-21 16:41:07 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
|
|
|
Static const char *uaudio_get_terminal_name(int);
|
|
|
|
#endif
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int uaudio_determine_class
|
|
|
|
(const struct io_terminal *, struct mixerctl *);
|
|
|
|
Static const char *uaudio_feature_name
|
|
|
|
(const struct io_terminal *, struct mixerctl *);
|
|
|
|
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 *);
|
2004-10-02 23:14:54 +04:00
|
|
|
|
|
|
|
Static int uaudio_signext(int, int);
|
|
|
|
Static int uaudio_value2bsd(struct mixerctl *, int);
|
|
|
|
Static int uaudio_bsd2value(struct mixerctl *, int);
|
|
|
|
Static int uaudio_get(struct uaudio_softc *, int, int, int, int, int);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int uaudio_ctl_get
|
|
|
|
(struct uaudio_softc *, int, struct mixerctl *, int);
|
|
|
|
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);
|
2004-10-02 23:14:54 +04:00
|
|
|
|
|
|
|
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 *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status uaudio_chan_alloc_buffers
|
|
|
|
(struct uaudio_softc *, struct chan *);
|
2004-10-02 23:14:54 +04:00
|
|
|
Static void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void uaudio_chan_init
|
|
|
|
(struct chan *, int, const struct audio_params *, int);
|
2004-10-02 23:14:54 +04:00
|
|
|
Static void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
|
|
|
|
Static void uaudio_chan_ptransfer(struct chan *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void uaudio_chan_pintr
|
|
|
|
(usbd_xfer_handle, usbd_private_handle, usbd_status);
|
2004-10-02 23:14:54 +04:00
|
|
|
|
|
|
|
Static void uaudio_chan_rtransfer(struct chan *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void uaudio_chan_rintr
|
|
|
|
(usbd_xfer_handle, usbd_private_handle, usbd_status);
|
2004-10-02 23:14:54 +04:00
|
|
|
|
|
|
|
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 *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int uaudio_set_params
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
(void *, int, int, struct audio_params *, struct audio_params *,
|
|
|
|
stream_filter_list_t *, stream_filter_list_t *);
|
|
|
|
Static int uaudio_round_blocksize(void *, int, int, const audio_params_t *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int uaudio_trigger_output
|
|
|
|
(void *, void *, void *, int, void (*)(void *), void *,
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
const audio_params_t *);
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int uaudio_trigger_input
|
|
|
|
(void *, void *, void *, int, void (*)(void *), void *,
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
const audio_params_t *);
|
2004-10-02 23:14:54 +04:00
|
|
|
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 *);
|
2011-11-24 03:07:28 +04:00
|
|
|
Static void uaudio_get_locks(void *, kmutex_t **, kmutex_t **);
|
2000-03-27 16:33:53 +04:00
|
|
|
|
2004-10-29 16:57:15 +04:00
|
|
|
Static const struct audio_hw_if uaudio_hw_if = {
|
1999-09-09 16:28:25 +04:00
|
|
|
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,
|
2001-10-03 04:04:47 +04:00
|
|
|
NULL,
|
2011-11-24 03:07:28 +04:00
|
|
|
uaudio_get_locks,
|
1999-09-09 16:28:25 +04:00
|
|
|
};
|
|
|
|
|
2008-05-24 20:40:58 +04:00
|
|
|
int uaudio_match(device_t, cfdata_t, void *);
|
2008-02-18 08:24:24 +03:00
|
|
|
void uaudio_attach(device_t, device_t, void *);
|
|
|
|
int uaudio_detach(device_t, int);
|
|
|
|
void uaudio_childdet(device_t, device_t);
|
|
|
|
int uaudio_activate(device_t, enum devact);
|
|
|
|
|
|
|
|
extern struct cfdriver uaudio_cd;
|
|
|
|
|
2008-05-24 20:40:58 +04:00
|
|
|
CFATTACH_DECL2_NEW(uaudio, sizeof(struct uaudio_softc),
|
2008-02-18 08:24:24 +03:00
|
|
|
uaudio_match, uaudio_attach, uaudio_detach, uaudio_activate, NULL,
|
|
|
|
uaudio_childdet);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-12-23 04:51:43 +04:00
|
|
|
int
|
2010-11-04 01:34:23 +03:00
|
|
|
uaudio_match(device_t parent, cfdata_t match, void *aux)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2010-11-04 01:34:23 +03:00
|
|
|
struct usbif_attach_arg *uaa = aux;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* Trigger on the control interface. */
|
Introduce different autoconf interface attributes for USB drivers
matching (and handling) a whole device and those which match an
interface only. This will allow to enforce some rules, eg that
the former don't use interface information for matching or that the
latter don't modify global device state.
The previous way left too much freedom do the drivers which led to
inconsistencies and abuse.
For now, I've not changed locators and submatch rules, this will
happen later.
There should not be any change in behaviour, except in the case of
some drivers which did behave inconsistently:
if_atu, if_axe, uep: matched the configured device in the interface
stage, but did configuration again. I've converted them to match
in the device stage.
ustir, utoppy: matched in the interface stage, but only against
vendor/device information, and used any configuration/interface
without checking. Changed to match in device stage, and added
some simple code to configure and use the first interface.
If you have one of those devices, please test!
2007-03-13 16:51:53 +03:00
|
|
|
if (uaa->class != UICLASS_AUDIO ||
|
|
|
|
uaa->subclass != UISUBCLASS_AUDIOCONTROL ||
|
1999-11-16 01:04:14 +03:00
|
|
|
(usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO))
|
2005-01-15 18:19:51 +03:00
|
|
|
return UMATCH_NONE;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return UMATCH_IFACECLASS_IFACESUBCLASS;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2011-12-23 04:51:43 +04:00
|
|
|
void
|
2010-11-04 01:34:23 +03:00
|
|
|
uaudio_attach(device_t parent, device_t self, void *aux)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2010-11-04 01:34:23 +03:00
|
|
|
struct uaudio_softc *sc = device_private(self);
|
|
|
|
struct usbif_attach_arg *uaa = aux;
|
1999-09-09 16:28:25 +04:00
|
|
|
usb_interface_descriptor_t *id;
|
|
|
|
usb_config_descriptor_t *cdesc;
|
2005-05-11 14:02:28 +04:00
|
|
|
char *devinfop;
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
2001-01-04 09:20:49 +03:00
|
|
|
int i, j, found;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2008-05-24 20:40:58 +04:00
|
|
|
sc->sc_dev = self;
|
2009-09-23 23:07:19 +04:00
|
|
|
sc->sc_udev = uaa->device;
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
|
|
|
|
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
|
2008-05-24 20:40:58 +04:00
|
|
|
|
2010-12-24 23:49:55 +03:00
|
|
|
strlcpy(sc->sc_adev.name, "USB audio", sizeof(sc->sc_adev.name));
|
|
|
|
strlcpy(sc->sc_adev.version, "", sizeof(sc->sc_adev.version));
|
|
|
|
snprintf(sc->sc_adev.config, sizeof(sc->sc_adev.config), "usb:%08x",
|
|
|
|
sc->sc_udev->cookie.cookie);
|
|
|
|
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_naive("\n");
|
2009-09-23 23:07:19 +04:00
|
|
|
aprint_normal("\n");
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2009-09-23 23:07:19 +04:00
|
|
|
devinfop = usbd_devinfo_alloc(uaa->device, 0);
|
|
|
|
aprint_normal_dev(self, "%s\n", devinfop);
|
|
|
|
usbd_devinfo_free(devinfop);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
cdesc = usbd_get_config_descriptor(sc->sc_udev);
|
1999-11-26 04:38:40 +03:00
|
|
|
if (cdesc == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(self,
|
|
|
|
"failed to get configuration descriptor\n");
|
2010-11-04 01:34:23 +03:00
|
|
|
return;
|
1999-11-26 04:38:40 +03:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_identify(sc, cdesc);
|
|
|
|
if (err) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(self,
|
|
|
|
"audio descriptors make no sense, error=%d\n", err);
|
2010-11-04 01:34:23 +03:00
|
|
|
return;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_ac_ifaceh = uaa->iface;
|
|
|
|
/* Pick up the AS interface. */
|
|
|
|
for (i = 0; i < uaa->nifaces; i++) {
|
2001-01-04 09:20:49 +03:00
|
|
|
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;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
}
|
2001-01-04 09:20:49 +03:00
|
|
|
if (found)
|
|
|
|
uaa->ifaces[i] = NULL;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2001-01-04 09:20:49 +03:00
|
|
|
for (j = 0; j < sc->sc_nalts; j++) {
|
|
|
|
if (sc->sc_alts[j].ifaceh == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(self,
|
|
|
|
"alt %d missing AS interface(s)\n", j);
|
2010-11-04 01:34:23 +03:00
|
|
|
return;
|
2001-01-04 09:20:49 +03:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_normal_dev(self, "audio rev %d.%02x\n",
|
1999-11-02 19:54:27 +03:00
|
|
|
sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff);
|
|
|
|
|
2002-02-12 22:52:43 +03:00
|
|
|
sc->sc_playchan.sc = sc->sc_recchan.sc = sc;
|
2002-05-18 19:14:39 +04:00
|
|
|
sc->sc_playchan.altidx = -1;
|
|
|
|
sc->sc_recchan.altidx = -1;
|
1999-11-09 19:52:14 +03:00
|
|
|
|
2000-12-28 14:56:22 +03:00
|
|
|
if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC)
|
2002-02-12 22:52:43 +03:00
|
|
|
sc->sc_altflags |= UA_NOFRAC;
|
2000-12-28 14:56:22 +03:00
|
|
|
|
2001-01-21 03:07:13 +03:00
|
|
|
#ifndef UAUDIO_DEBUG
|
|
|
|
if (bootverbose)
|
|
|
|
#endif
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_normal_dev(self, "%d mixer controls\n",
|
2001-01-21 03:07:13 +03:00
|
|
|
sc->sc_nctls);
|
|
|
|
|
2001-01-23 17:04:13 +03:00
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
|
2010-11-04 01:34:23 +03:00
|
|
|
sc->sc_dev);
|
2001-01-23 17:04:13 +03:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%s", "doing audio_attach_mi\n");
|
2008-05-24 20:40:58 +04:00
|
|
|
sc->sc_audiodev = audio_attach_mi(&uaudio_hw_if, sc, sc->sc_dev);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2010-11-04 01:34:23 +03:00
|
|
|
return;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-11-12 22:50:01 +03:00
|
|
|
uaudio_activate(device_t self, enum devact act)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2009-11-12 22:50:01 +03:00
|
|
|
struct uaudio_softc *sc = device_private(self);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
sc->sc_dying = 1;
|
2009-11-12 22:50:01 +03:00
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return EOPNOTSUPP;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-18 08:24:24 +03:00
|
|
|
void
|
|
|
|
uaudio_childdet(device_t self, device_t child)
|
|
|
|
{
|
|
|
|
struct uaudio_softc *sc = device_private(self);
|
|
|
|
|
|
|
|
KASSERT(sc->sc_audiodev == child);
|
|
|
|
sc->sc_audiodev = NULL;
|
|
|
|
}
|
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
int
|
2008-02-18 08:24:24 +03:00
|
|
|
uaudio_detach(device_t self, int flags)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2008-02-18 08:24:24 +03:00
|
|
|
struct uaudio_softc *sc = device_private(self);
|
2005-01-15 18:19:51 +03:00
|
|
|
int rv;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
rv = 0;
|
1999-11-09 19:52:14 +03:00
|
|
|
/* Wait for outstanding requests to complete. */
|
|
|
|
usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES);
|
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
if (sc->sc_audiodev != NULL)
|
1999-09-09 16:28:25 +04:00
|
|
|
rv = config_detach(sc->sc_audiodev, flags);
|
|
|
|
|
2000-02-02 16:18:45 +03:00
|
|
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
|
2010-11-04 01:34:23 +03:00
|
|
|
sc->sc_dev);
|
2000-02-02 16:18:45 +03:00
|
|
|
|
2004-11-13 18:00:48 +03:00
|
|
|
if (sc->sc_formats != NULL)
|
|
|
|
free(sc->sc_formats, M_USBDEV);
|
|
|
|
auconv_delete_encodings(sc->sc_encodings);
|
2011-11-24 03:07:28 +04:00
|
|
|
|
|
|
|
mutex_destroy(&sc->sc_lock);
|
|
|
|
mutex_destroy(&sc->sc_intr_lock);
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return rv;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_query_encoding(void *addr, struct audio_encoding *fp)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
|
|
|
int flags;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
|
|
|
flags = sc->sc_altflags;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_nalts == 0 || flags == 0)
|
2005-01-15 18:19:51 +03:00
|
|
|
return ENXIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-11-13 18:00:48 +03:00
|
|
|
return auconv_query_encoding(sc->sc_encodings, fp);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static const usb_interface_descriptor_t *
|
2005-05-30 08:21:39 +04:00
|
|
|
uaudio_find_iface(const char *tbuf, int size, int *offsp, int subtype)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2004-10-02 23:14:54 +04:00
|
|
|
const usb_interface_descriptor_t *d;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
while (*offsp < size) {
|
2005-05-30 08:21:39 +04:00
|
|
|
d = (const void *)(tbuf + *offsp);
|
1999-09-09 16:28:25 +04:00
|
|
|
*offsp += d->bLength;
|
|
|
|
if (d->bDescriptorType == UDESC_INTERFACE &&
|
2000-03-01 00:37:00 +03:00
|
|
|
d->bInterfaceClass == UICLASS_AUDIO &&
|
1999-09-09 16:28:25 +04:00
|
|
|
d->bInterfaceSubClass == subtype)
|
2005-01-15 18:19:51 +03:00
|
|
|
return d;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2005-01-15 18:19:51 +03:00
|
|
|
return NULL;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2000-12-29 16:28:41 +03:00
|
|
|
int res;
|
2004-10-22 19:34:04 +04:00
|
|
|
size_t len;
|
|
|
|
struct mixerctl *nmc;
|
2000-12-29 16:28:41 +03:00
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
if (mc->class < UAC_NCLASSES) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("adding %s.%s\n", uac_names[mc->class], mc->ctlname);
|
2004-10-21 16:41:07 +04:00
|
|
|
} else {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("adding %s\n", mc->ctlname);
|
2004-10-21 16:41:07 +04:00
|
|
|
}
|
2004-10-22 19:34:04 +04:00
|
|
|
len = sizeof(*mc) * (sc->sc_nctls + 1);
|
|
|
|
nmc = malloc(len, M_USBDEV, M_NOWAIT);
|
2001-05-12 23:18:57 +04:00
|
|
|
if (nmc == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_mixer_add_ctl: no memory\n");
|
1999-09-09 16:28:25 +04:00
|
|
|
return;
|
|
|
|
}
|
2004-10-22 19:34:04 +04:00
|
|
|
/* 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);
|
|
|
|
}
|
2001-05-12 23:18:57 +04:00
|
|
|
sc->sc_ctls = nmc;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2000-12-29 16:28:41 +03:00
|
|
|
mc->delta = 0;
|
2004-10-03 10:01:09 +04:00
|
|
|
if (mc->type == MIX_ON_OFF) {
|
|
|
|
mc->minval = 0;
|
|
|
|
mc->maxval = 1;
|
|
|
|
} else if (mc->type == MIX_SELECTOR) {
|
|
|
|
;
|
|
|
|
} else {
|
1999-09-09 16:28:25 +04:00
|
|
|
/* Determine min and max values. */
|
2002-03-23 20:17:10 +03:00
|
|
|
mc->minval = uaudio_signext(mc->type,
|
|
|
|
uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE,
|
|
|
|
mc->wValue[0], mc->wIndex,
|
1999-09-09 16:28:25 +04:00
|
|
|
MIX_SIZE(mc->type)));
|
2002-03-23 20:17:10 +03:00
|
|
|
mc->maxval = 1 + uaudio_signext(mc->type,
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE,
|
|
|
|
mc->wValue[0], mc->wIndex,
|
|
|
|
MIX_SIZE(mc->type)));
|
2000-12-29 17:49:28 +03:00
|
|
|
mc->mul = mc->maxval - mc->minval;
|
|
|
|
if (mc->mul == 0)
|
|
|
|
mc->mul = 1;
|
2000-12-29 16:28:41 +03:00
|
|
|
res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE,
|
|
|
|
mc->wValue[0], mc->wIndex,
|
|
|
|
MIX_SIZE(mc->type));
|
2000-12-29 17:49:28 +03:00
|
|
|
if (res > 0)
|
2003-10-14 17:12:19 +04:00
|
|
|
mc->delta = (res * 255 + mc->mul/2) / mc->mul;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_ctls[sc->sc_nctls++] = *mc;
|
|
|
|
|
1999-10-14 05:18:39 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
1999-09-09 16:28:25 +04:00
|
|
|
if (uaudiodebug > 2) {
|
|
|
|
int i;
|
2011-11-27 08:10:38 +04:00
|
|
|
|
|
|
|
DPRINTFN_CLEAN(2, "wValue=%04x", mc->wValue[0]);
|
1999-09-09 16:28:25 +04:00
|
|
|
for (i = 1; i < mc->nchan; i++)
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN_CLEAN(2, ",%04x", mc->wValue[i]);
|
|
|
|
DPRINTFN_CLEAN(2, " wIndex=%04x type=%d name='%s' unit='%s' "
|
1999-09-09 16:28:25 +04:00
|
|
|
"min=%d max=%d\n",
|
|
|
|
mc->wIndex, mc->type, mc->ctlname, mc->ctlunit,
|
2011-11-27 08:10:38 +04:00
|
|
|
mc->minval, mc->maxval);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static char *
|
2006-11-16 04:32:37 +03:00
|
|
|
uaudio_id_name(struct uaudio_softc *sc,
|
|
|
|
const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-05-30 08:21:39 +04:00
|
|
|
static char tbuf[32];
|
2005-01-15 18:19:51 +03:00
|
|
|
|
2005-05-30 08:21:39 +04:00
|
|
|
snprintf(tbuf, sizeof(tbuf), "i%d", id);
|
|
|
|
return tbuf;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-11-05 20:46:14 +03:00
|
|
|
#ifdef UAUDIO_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);
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("cluster: bNrChannels=%u wChannelConfig=0x%.4x",
|
2004-11-05 20:46:14 +03:00
|
|
|
cl->bNrChannels, cc);
|
|
|
|
first = TRUE;
|
|
|
|
for (i = 0; cc != 0; i++) {
|
|
|
|
if (cc & 1) {
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%c%s", first ? '<' : ',', channel_names[i]);
|
2004-11-05 20:46:14 +03:00
|
|
|
first = FALSE;
|
|
|
|
}
|
|
|
|
cc = cc >> 1;
|
|
|
|
}
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("> iChannelNames=%u", cl->iChannelNames);
|
2004-11-05 20:46:14 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static struct usb_audio_cluster
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_get_cluster(int id, const struct io_terminal *iot)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
struct usb_audio_cluster r;
|
2007-02-26 16:14:11 +03:00
|
|
|
const uaudio_cs_descriptor_t *dp;
|
1999-09-09 16:28:25 +04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 25; i++) { /* avoid infinite loops */
|
2004-10-21 16:41:07 +04:00
|
|
|
dp = iot[id].d.desc;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (dp == 0)
|
|
|
|
goto bad;
|
|
|
|
switch (dp->bDescriptorSubtype) {
|
|
|
|
case UDESCSUB_AC_INPUT:
|
2004-10-21 16:41:07 +04:00
|
|
|
r.bNrChannels = iot[id].d.it->bNrChannels;
|
|
|
|
USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig));
|
|
|
|
r.iChannelNames = iot[id].d.it->iChannelNames;
|
2005-01-15 18:19:51 +03:00
|
|
|
return r;
|
1999-09-09 16:28:25 +04:00
|
|
|
case UDESCSUB_AC_OUTPUT:
|
2004-10-21 16:41:07 +04:00
|
|
|
id = iot[id].d.ot->bSourceId;
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_MIXER:
|
2005-05-30 08:21:39 +04:00
|
|
|
r = *(const struct usb_audio_cluster *)
|
2004-10-21 16:41:07 +04:00
|
|
|
&iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
|
2005-01-15 18:19:51 +03:00
|
|
|
return r;
|
1999-09-09 16:28:25 +04:00
|
|
|
case UDESCSUB_AC_SELECTOR:
|
|
|
|
/* XXX This is not really right */
|
2004-10-21 16:41:07 +04:00
|
|
|
id = iot[id].d.su->baSourceId[0];
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_FEATURE:
|
2004-10-21 16:41:07 +04:00
|
|
|
id = iot[id].d.fu->bSourceId;
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_PROCESSING:
|
2005-05-30 08:21:39 +04:00
|
|
|
r = *(const struct usb_audio_cluster *)
|
2004-10-21 16:41:07 +04:00
|
|
|
&iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
|
2005-01-15 18:19:51 +03:00
|
|
|
return r;
|
1999-09-09 16:28:25 +04:00
|
|
|
case UDESCSUB_AC_EXTENSION:
|
2005-05-30 08:21:39 +04:00
|
|
|
r = *(const struct usb_audio_cluster *)
|
2004-10-21 16:41:07 +04:00
|
|
|
&iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
|
2005-01-15 18:19:51 +03:00
|
|
|
return r;
|
1999-09-09 16:28:25 +04:00
|
|
|
default:
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bad:
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_get_cluster: bad data\n");
|
1999-09-09 16:28:25 +04:00
|
|
|
memset(&r, 0, sizeof r);
|
2005-01-15 18:19:51 +03:00
|
|
|
return r;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_input_terminal *d;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.it;
|
2004-11-13 18:00:48 +03:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x "
|
1999-09-09 16:28:25 +04:00
|
|
|
"bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
|
|
|
|
"iChannelNames=%d iTerminal=%d\n",
|
|
|
|
d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
|
|
|
|
d->bNrChannels, UGETW(d->wChannelConfig),
|
2011-11-27 08:10:38 +04:00
|
|
|
d->iChannelNames, d->iTerminal);
|
1999-09-09 16:28:25 +04:00
|
|
|
#endif
|
2004-11-13 18:00:48 +03:00
|
|
|
/* If USB input terminal, record wChannelConfig */
|
|
|
|
if ((UGETW(d->wTerminalType) & 0xff00) != 0x0100)
|
|
|
|
return;
|
|
|
|
sc->sc_channel_config = UGETW(d->wChannelConfig);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2006-11-16 04:32:37 +03:00
|
|
|
uaudio_add_output(struct uaudio_softc *sc,
|
|
|
|
const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
1999-10-14 05:18:39 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_output_terminal *d;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.ot;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x "
|
1999-09-09 16:28:25 +04:00
|
|
|
"bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
|
|
|
|
d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
|
2011-11-27 08:10:38 +04:00
|
|
|
d->bSourceId, d->iTerminal);
|
1999-09-09 16:28:25 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_mixer_unit *d;
|
2005-05-30 08:21:39 +04:00
|
|
|
const struct usb_audio_mixer_unit_1 *d1;
|
1999-09-09 16:28:25 +04:00
|
|
|
int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k;
|
2005-05-30 08:21:39 +04:00
|
|
|
const uByte *bm;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct mixerctl mix;
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.mu;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n",
|
|
|
|
d->bUnitId, d->bNrInPins);
|
2002-03-23 20:17:10 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
/* Compute the number of input channels */
|
|
|
|
ichs = 0;
|
|
|
|
for (i = 0; i < d->bNrInPins; i++)
|
2004-10-21 16:41:07 +04:00
|
|
|
ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* and the number of output channels */
|
2005-05-30 08:21:39 +04:00
|
|
|
d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
|
1999-09-09 16:28:25 +04:00
|
|
|
ochs = d1->bNrChannels;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"ichs=%d ochs=%d\n", ichs, ochs);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
bm = d1->bmControls;
|
|
|
|
mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_determine_class(&iot[id], &mix);
|
1999-09-09 16:28:25 +04:00
|
|
|
mix.type = MIX_SIGNED_16;
|
|
|
|
mix.ctlunit = AudioNvolume;
|
2006-03-08 03:14:20 +03:00
|
|
|
#define _BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
|
1999-09-09 16:28:25 +04:00
|
|
|
for (p = i = 0; i < d->bNrInPins; i++) {
|
2004-10-21 16:41:07 +04:00
|
|
|
chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
|
1999-09-09 16:28:25 +04:00
|
|
|
mc = 0;
|
|
|
|
for (c = 0; c < chs; c++) {
|
|
|
|
mo = 0;
|
|
|
|
for (o = 0; o < ochs; o++) {
|
|
|
|
bno = (p + c) * ochs + o;
|
2006-03-08 03:14:20 +03:00
|
|
|
if (_BIT(bno))
|
1999-09-09 16:28:25 +04:00
|
|
|
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;
|
2006-03-08 03:14:20 +03:00
|
|
|
if (_BIT(bno))
|
2002-03-23 20:17:10 +03:00
|
|
|
mix.wValue[k++] =
|
1999-09-09 16:28:25 +04:00
|
|
|
MAKE(p+c+1, o+1);
|
|
|
|
}
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s",
|
2004-10-21 16:41:07 +04:00
|
|
|
d->bUnitId, uaudio_id_name(sc, iot,
|
2004-04-22 04:17:10 +04:00
|
|
|
d->baSourceId[i]));
|
1999-09-09 16:28:25 +04:00
|
|
|
mix.nchan = chs;
|
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
|
|
|
} else {
|
|
|
|
/* XXX */
|
|
|
|
}
|
2006-03-08 03:14:20 +03:00
|
|
|
#undef _BIT
|
1999-09-09 16:28:25 +04:00
|
|
|
p += chs;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_selector_unit *d;
|
2004-10-03 10:01:09 +04:00
|
|
|
struct mixerctl mix;
|
|
|
|
int i, wp;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.su;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n",
|
|
|
|
d->bUnitId, d->bNrInPins);
|
2004-10-03 10:01:09 +04:00
|
|
|
mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
|
|
|
|
mix.wValue[0] = MAKE(0, 0);
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_determine_class(&iot[id], &mix);
|
2004-10-03 10:01:09 +04:00
|
|
|
mix.nchan = 1;
|
|
|
|
mix.type = MIX_SELECTOR;
|
|
|
|
mix.ctlunit = "";
|
|
|
|
mix.minval = 1;
|
|
|
|
mix.maxval = d->bNrInPins;
|
|
|
|
mix.mul = mix.maxval - mix.minval;
|
2004-10-20 17:48:32 +04:00
|
|
|
wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
|
2004-10-03 10:01:09 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2004-10-22 20:01:49 +04:00
|
|
|
Static const char *
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_get_terminal_name(int terminal_type)
|
|
|
|
{
|
2005-05-30 08:21:39 +04:00
|
|
|
static char tbuf[100];
|
2004-10-21 16:41:07 +04:00
|
|
|
|
|
|
|
switch (terminal_type) {
|
2004-10-22 21:00:22 +04:00
|
|
|
/* USB terminal types */
|
|
|
|
case UAT_UNDEFINED: return "UAT_UNDEFINED";
|
2004-10-21 16:41:07 +04:00
|
|
|
case UAT_STREAM: return "UAT_STREAM";
|
2004-10-22 21:00:22 +04:00
|
|
|
case UAT_VENDOR: return "UAT_VENDOR";
|
|
|
|
/* input terminal types */
|
|
|
|
case UATI_UNDEFINED: return "UATI_UNDEFINED";
|
2004-10-21 16:41:07 +04:00
|
|
|
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";
|
2004-10-22 21:00:22 +04:00
|
|
|
/* output terminal types */
|
|
|
|
case UATO_UNDEFINED: return "UATO_UNDEFINED";
|
2004-10-21 16:41:07 +04:00
|
|
|
case UATO_SPEAKER: return "UATO_SPEAKER";
|
2004-10-22 21:00:22 +04:00
|
|
|
case UATO_HEADPHONES: return "UATO_HEADPHONES";
|
|
|
|
case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO";
|
2004-10-21 16:41:07 +04:00
|
|
|
case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER";
|
|
|
|
case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER";
|
|
|
|
case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER";
|
|
|
|
case UATO_SUBWOOFER: return "UATO_SUBWOOFER";
|
2004-10-22 21:00:22 +04:00
|
|
|
/* bidir terminal types */
|
2004-10-21 16:41:07 +04:00
|
|
|
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";
|
2004-10-22 21:00:22 +04:00
|
|
|
/* telephony terminal types */
|
2004-10-21 16:41:07 +04:00
|
|
|
case UATT_UNDEFINED: return "UATT_UNDEFINED";
|
|
|
|
case UATT_PHONELINE: return "UATT_PHONELINE";
|
|
|
|
case UATT_TELEPHONE: return "UATT_TELEPHONE";
|
|
|
|
case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE";
|
2004-10-22 21:00:22 +04:00
|
|
|
/* external terminal types */
|
2004-10-21 16:41:07 +04:00
|
|
|
case UATE_UNDEFINED: return "UATE_UNDEFINED";
|
2004-10-22 21:00:22 +04:00
|
|
|
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 */
|
2004-10-21 16:41:07 +04:00
|
|
|
case UATF_UNDEFINED: return "UATF_UNDEFINED";
|
|
|
|
case UATF_CALIBNOISE: return "UATF_CALIBNOISE";
|
|
|
|
case UATF_EQUNOISE: return "UATF_EQUNOISE";
|
2004-10-22 21:00:22 +04:00
|
|
|
case UATF_CDPLAYER: return "UATF_CDPLAYER";
|
2004-10-21 16:41:07 +04:00
|
|
|
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";
|
2004-10-22 21:00:22 +04:00
|
|
|
case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO";
|
|
|
|
case UATF_DVDAUDIO: return "UATF_DVDAUDIO";
|
|
|
|
case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO";
|
2004-10-21 16:41:07 +04:00
|
|
|
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";
|
2004-10-22 21:00:22 +04:00
|
|
|
case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER";
|
2004-10-21 16:41:07 +04:00
|
|
|
default:
|
2005-05-30 08:21:39 +04:00
|
|
|
snprintf(tbuf, sizeof(tbuf), "unknown type (0x%.4x)", terminal_type);
|
|
|
|
return tbuf;
|
2004-10-21 16:41:07 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
/*
|
2004-10-22 19:25:56 +04:00
|
|
|
* If the ultimate destination of the unit is just one output
|
2004-10-21 16:41:07 +04:00
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static const char *
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("terminal_type=%s\n", uaudio_get_terminal_name(terminal_type));
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
|
|
|
|
2004-10-22 21:00:22 +04:00
|
|
|
case UATO_SUBWOOFER:
|
|
|
|
return AudioNlfe;
|
|
|
|
|
|
|
|
/* telephony terminal types */
|
|
|
|
case UATT_UNDEFINED:
|
|
|
|
case UATT_PHONELINE:
|
|
|
|
case UATT_TELEPHONE:
|
|
|
|
case UATT_DOWNLINEPHONE:
|
|
|
|
return "phone";
|
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
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:
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("'master' for 0x%.4x\n", terminal_type);
|
2004-10-21 16:41:07 +04:00
|
|
|
return AudioNmaster;
|
|
|
|
}
|
|
|
|
return AudioNmaster;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_feature_unit *d;
|
|
|
|
const uByte *ctls;
|
|
|
|
int ctlsize;
|
|
|
|
int nchan;
|
1999-09-09 16:28:25 +04:00
|
|
|
u_int fumask, mmask, cmask;
|
|
|
|
struct mixerctl mix;
|
|
|
|
int chan, ctl, i, unit;
|
2004-10-21 16:41:07 +04:00
|
|
|
const char *mixername;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
#define GET(i) (ctls[(i)*ctlsize] | \
|
|
|
|
(ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.fu;
|
|
|
|
ctls = d->bmaControls;
|
|
|
|
ctlsize = d->bControlSize;
|
|
|
|
nchan = (d->bLength - 7) / ctlsize;
|
1999-09-09 16:28:25 +04:00
|
|
|
mmask = GET(0);
|
|
|
|
/* Figure out what we can control */
|
|
|
|
for (cmask = 0, chan = 1; chan < nchan; chan++) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(9,"chan=%d mask=%x\n",
|
|
|
|
chan, GET(chan));
|
1999-09-09 16:28:25 +04:00
|
|
|
cmask |= GET(chan);
|
|
|
|
}
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(1,"bUnitId=%d, "
|
2002-03-23 20:17:10 +03:00
|
|
|
"%d channels, mmask=0x%04x, cmask=0x%04x\n",
|
2011-11-27 08:10:38 +04:00
|
|
|
d->bUnitId, nchan, mmask, cmask);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
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);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(4,"ctl=%d fumask=0x%04x\n",
|
|
|
|
ctl, fumask);
|
1999-09-09 16:28:25 +04:00
|
|
|
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
|
2004-10-21 16:41:07 +04:00
|
|
|
mixername = uaudio_feature_name(&iot[id], &mix);
|
1999-09-09 16:28:25 +04:00
|
|
|
switch (ctl) {
|
|
|
|
case MUTE_CONTROL:
|
|
|
|
mix.type = MIX_ON_OFF;
|
2003-02-16 21:16:07 +03:00
|
|
|
mix.ctlunit = "";
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
2004-10-21 16:41:07 +04:00
|
|
|
"%s.%s", mixername, AudioNmute);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case VOLUME_CONTROL:
|
|
|
|
mix.type = MIX_SIGNED_16;
|
2003-02-16 21:16:07 +03:00
|
|
|
mix.ctlunit = AudioNvolume;
|
2004-10-21 16:41:07 +04:00
|
|
|
strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case BASS_CONTROL:
|
|
|
|
mix.type = MIX_SIGNED_8;
|
2003-02-16 21:16:07 +03:00
|
|
|
mix.ctlunit = AudioNbass;
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
2004-10-21 16:41:07 +04:00
|
|
|
"%s.%s", mixername, AudioNbass);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case MID_CONTROL:
|
|
|
|
mix.type = MIX_SIGNED_8;
|
2003-02-16 21:16:07 +03:00
|
|
|
mix.ctlunit = AudioNmid;
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
2004-10-21 16:41:07 +04:00
|
|
|
"%s.%s", mixername, AudioNmid);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case TREBLE_CONTROL:
|
|
|
|
mix.type = MIX_SIGNED_8;
|
2003-02-16 21:16:07 +03:00
|
|
|
mix.ctlunit = AudioNtreble;
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
2004-10-21 16:41:07 +04:00
|
|
|
"%s.%s", mixername, AudioNtreble);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case GRAPHIC_EQUALIZER_CONTROL:
|
1999-11-02 19:54:27 +03:00
|
|
|
continue; /* XXX don't add anything */
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case AGC_CONTROL:
|
|
|
|
mix.type = MIX_ON_OFF;
|
|
|
|
mix.ctlunit = "";
|
2004-10-21 16:41:07 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
|
|
|
|
mixername, AudioNagc);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case DELAY_CONTROL:
|
|
|
|
mix.type = MIX_UNSIGNED_16;
|
|
|
|
mix.ctlunit = "4 ms";
|
2004-10-21 16:41:07 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
|
|
|
"%s.%s", mixername, AudioNdelay);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case BASS_BOOST_CONTROL:
|
|
|
|
mix.type = MIX_ON_OFF;
|
|
|
|
mix.ctlunit = "";
|
2004-10-21 16:41:07 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
|
|
|
"%s.%s", mixername, AudioNbassboost);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case LOUDNESS_CONTROL:
|
|
|
|
mix.type = MIX_ON_OFF;
|
|
|
|
mix.ctlunit = "";
|
2004-10-21 16:41:07 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname),
|
|
|
|
"%s.%s", mixername, AudioNloudness);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-02 23:14:54 +04:00
|
|
|
uaudio_add_processing_updown(struct uaudio_softc *sc,
|
2004-10-21 16:41:07 +04:00
|
|
|
const struct io_terminal *iot, int id)
|
2000-01-16 12:32:56 +03:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_processing_unit *d;
|
|
|
|
const struct usb_audio_processing_unit_1 *d1;
|
|
|
|
const struct usb_audio_processing_unit_updown *ud;
|
2000-01-16 12:32:56 +03:00
|
|
|
struct mixerctl mix;
|
|
|
|
int i;
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
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];
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bUnitId=%d bNrModes=%d\n",
|
|
|
|
d->bUnitId, ud->bNrModes);
|
2000-01-16 12:32:56 +03:00
|
|
|
|
|
|
|
if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%s", "no mode select\n");
|
2000-01-16 12:32:56 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
|
|
|
|
mix.nchan = 1;
|
|
|
|
mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0);
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_determine_class(&iot[id], &mix);
|
2000-01-16 12:32:56 +03:00
|
|
|
mix.type = MIX_ON_OFF; /* XXX */
|
|
|
|
mix.ctlunit = "";
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId);
|
2000-01-16 12:32:56 +03:00
|
|
|
|
|
|
|
for (i = 0; i < ud->bNrModes; i++) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"i=%d bm=0x%x\n",
|
|
|
|
i, UGETW(ud->waModes[i]));
|
2000-01-16 12:32:56 +03:00
|
|
|
/* XXX */
|
|
|
|
}
|
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_processing_unit *d;
|
|
|
|
const struct usb_audio_processing_unit_1 *d1;
|
|
|
|
int ptype;
|
2000-01-07 00:13:55 +03:00
|
|
|
struct mixerctl mix;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.pu;
|
|
|
|
d1 = (const struct usb_audio_processing_unit_1 *)
|
|
|
|
&d->baSourceId[d->bNrInPins];
|
|
|
|
ptype = UGETW(d->wProcessType);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"wProcessType=%d bUnitId=%d "
|
|
|
|
"bNrInPins=%d\n", ptype, d->bUnitId, d->bNrInPins);
|
2000-01-07 00:13:55 +03:00
|
|
|
|
|
|
|
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);
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_determine_class(&iot[id], &mix);
|
2000-01-07 00:13:55 +03:00
|
|
|
mix.type = MIX_ON_OFF;
|
|
|
|
mix.ctlunit = "";
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable",
|
|
|
|
d->bUnitId, ptype);
|
2000-01-07 00:13:55 +03:00
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(ptype) {
|
|
|
|
case UPDOWNMIX_PROCESS:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_processing_updown(sc, iot, id);
|
2000-01-16 12:32:56 +03:00
|
|
|
break;
|
2000-01-07 00:13:55 +03:00
|
|
|
case DOLBY_PROLOGIC_PROCESS:
|
|
|
|
case P3D_STEREO_EXTENDER_PROCESS:
|
|
|
|
case REVERBATION_PROCESS:
|
|
|
|
case CHORUS_PROCESS:
|
|
|
|
case DYN_RANGE_COMP_PROCESS:
|
|
|
|
default:
|
|
|
|
#ifdef UAUDIO_DEBUG
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug(
|
|
|
|
"uaudio_add_processing: unit %d, type=%d not impl.\n",
|
|
|
|
d->bUnitId, ptype);
|
1999-09-09 16:28:25 +04:00
|
|
|
#endif
|
2000-01-07 00:13:55 +03:00
|
|
|
break;
|
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
const struct usb_audio_extension_unit *d;
|
|
|
|
const struct usb_audio_extension_unit_1 *d1;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct mixerctl mix;
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
d = iot[id].d.eu;
|
|
|
|
d1 = (const struct usb_audio_extension_unit_1 *)
|
|
|
|
&d->baSourceId[d->bNrInPins];
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n",
|
|
|
|
d->bUnitId, d->bNrInPins);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2000-12-28 14:56:22 +03:00
|
|
|
if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_XU)
|
2000-08-24 18:11:09 +04:00
|
|
|
return;
|
|
|
|
|
2000-01-07 00:13:55 +03:00
|
|
|
if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
|
1999-09-09 16:28:25 +04:00
|
|
|
mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
|
|
|
|
mix.nchan = 1;
|
|
|
|
mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0);
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_determine_class(&iot[id], &mix);
|
1999-09-09 16:28:25 +04:00
|
|
|
mix.type = MIX_ON_OFF;
|
|
|
|
mix.ctlunit = "";
|
2004-04-22 04:17:10 +04:00
|
|
|
snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable",
|
|
|
|
d->bUnitId);
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_mixer_add_ctl(sc, &mix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static struct terminal_list*
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_merge_terminal_list: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static struct terminal_list *
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
|
|
|
|
if (tml == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_io_terminaltype: no memory\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2004-10-02 23:14:54 +04:00
|
|
|
uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_identify_ac(sc, cdesc);
|
|
|
|
if (err)
|
2005-01-15 18:19:51 +03:00
|
|
|
return err;
|
|
|
|
return uaudio_identify_as(sc, cdesc);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2004-10-02 23:14:54 +04:00
|
|
|
uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2004-10-22 19:34:04 +04:00
|
|
|
size_t len;
|
|
|
|
struct as_info *nai;
|
2001-05-12 23:18:57 +04:00
|
|
|
|
2004-10-22 19:34:04 +04:00
|
|
|
len = sizeof(*ai) * (sc->sc_nalts + 1);
|
|
|
|
nai = malloc(len, M_USBDEV, M_NOWAIT);
|
2001-05-12 23:18:57 +04:00
|
|
|
if (nai == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_add_alt: no memory\n");
|
1999-09-09 16:28:25 +04:00
|
|
|
return;
|
|
|
|
}
|
2004-10-22 19:34:04 +04:00
|
|
|
/* 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);
|
|
|
|
}
|
2001-05-12 23:18:57 +04:00
|
|
|
sc->sc_alts = nai;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"adding alt=%d, enc=%d\n",
|
|
|
|
ai->alt, ai->encoding);
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_alts[sc->sc_nalts++] = *ai;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2005-05-30 08:21:39 +04:00
|
|
|
uaudio_process_as(struct uaudio_softc *sc, const char *tbuf, int *offsp,
|
2004-10-02 23:14:54 +04:00
|
|
|
int size, const usb_interface_descriptor_t *id)
|
1999-09-09 16:28:25 +04:00
|
|
|
#define offs (*offsp)
|
|
|
|
{
|
2004-10-02 23:14:54 +04:00
|
|
|
const struct usb_audio_streaming_interface_descriptor *asid;
|
|
|
|
const struct usb_audio_streaming_type1_descriptor *asf1d;
|
|
|
|
const usb_endpoint_descriptor_audio_t *ed;
|
2004-10-16 22:08:50 +04:00
|
|
|
const usb_endpoint_descriptor_audio_t *epdesc1;
|
2004-10-02 23:14:54 +04:00
|
|
|
const struct usb_audio_streaming_endpoint_descriptor *sed;
|
1999-09-09 16:28:25 +04:00
|
|
|
int format, chan, prec, enc;
|
2004-10-16 22:08:50 +04:00
|
|
|
int dir, type, sync;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct as_info ai;
|
2004-10-02 23:14:54 +04:00
|
|
|
const char *format_str;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-05-30 08:21:39 +04:00
|
|
|
asid = (const void *)(tbuf + offs);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (asid->bDescriptorType != UDESC_CS_INTERFACE ||
|
|
|
|
asid->bDescriptorSubtype != AS_GENERAL)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("asid: bTerminakLink=%d wFormatTag=%d\n",
|
|
|
|
asid->bTerminalLink, UGETW(asid->wFormatTag));
|
1999-09-09 16:28:25 +04:00
|
|
|
offs += asid->bLength;
|
|
|
|
if (offs > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2004-10-02 23:14:54 +04:00
|
|
|
|
2005-05-30 08:21:39 +04:00
|
|
|
asf1d = (const void *)(tbuf + offs);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (asf1d->bDescriptorType != UDESC_CS_INTERFACE ||
|
|
|
|
asf1d->bDescriptorSubtype != FORMAT_TYPE)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
offs += asf1d->bLength;
|
|
|
|
if (offs > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-10-14 00:13:29 +04:00
|
|
|
if (asf1d->bFormatType != FORMAT_TYPE_I) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored setting with type %d format\n", UGETW(asid->wFormatTag));
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-10-14 00:13:29 +04:00
|
|
|
}
|
|
|
|
|
2005-05-30 08:21:39 +04:00
|
|
|
ed = (const void *)(tbuf + offs);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (ed->bDescriptorType != UDESC_ENDPOINT)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("endpoint[0] bLength=%d bDescriptorType=%d "
|
1999-10-25 14:16:49 +04:00
|
|
|
"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),
|
2011-11-27 08:10:38 +04:00
|
|
|
ed->bInterval, ed->bRefresh, ed->bSynchAddress);
|
1999-09-09 16:28:25 +04:00
|
|
|
offs += ed->bLength;
|
|
|
|
if (offs > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-10-25 14:16:49 +04:00
|
|
|
if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-10-25 14:16:49 +04:00
|
|
|
|
|
|
|
dir = UE_GET_DIR(ed->bEndpointAddress);
|
|
|
|
type = UE_GET_ISO_TYPE(ed->bmAttributes);
|
2000-12-28 14:56:22 +03:00
|
|
|
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;
|
|
|
|
|
2001-01-21 03:07:13 +03:00
|
|
|
/* We can't handle endpoints that need a sync pipe yet. */
|
2004-10-16 22:08:50 +04:00
|
|
|
sync = FALSE;
|
2004-10-02 23:14:54 +04:00
|
|
|
if (dir == UE_DIR_IN && type == UE_ISO_ADAPT) {
|
2004-10-16 22:08:50 +04:00
|
|
|
sync = TRUE;
|
|
|
|
#ifndef UAUDIO_MULTIPLE_ENDPOINTS
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored input endpoint of type adaptive\n");
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
2004-10-16 22:08:50 +04:00
|
|
|
#endif
|
2004-10-02 23:14:54 +04:00
|
|
|
}
|
|
|
|
if (dir != UE_DIR_IN && type == UE_ISO_ASYNC) {
|
2004-10-16 22:08:50 +04:00
|
|
|
sync = TRUE;
|
|
|
|
#ifndef UAUDIO_MULTIPLE_ENDPOINTS
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored output endpoint of type async\n");
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
2004-10-16 22:08:50 +04:00
|
|
|
#endif
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2005-05-30 08:21:39 +04:00
|
|
|
sed = (const void *)(tbuf + offs);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sed->bDescriptorType != UDESC_CS_ENDPOINT ||
|
|
|
|
sed->bDescriptorSubtype != AS_GENERAL)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF(" streadming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength);
|
1999-09-09 16:28:25 +04:00
|
|
|
offs += sed->bLength;
|
|
|
|
if (offs > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2006-04-14 21:00:55 +04:00
|
|
|
#ifdef UAUDIO_MULTIPLE_ENDPOINTS
|
2004-10-16 22:08:50 +04:00
|
|
|
if (sync && id->bNumEndpoints <= 1) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"a sync-pipe endpoint but no other endpoint\n");
|
2004-10-16 22:08:50 +04:00
|
|
|
return USBD_INVAL;
|
|
|
|
}
|
2006-04-14 21:00:55 +04:00
|
|
|
#endif
|
2004-10-16 22:08:50 +04:00
|
|
|
if (!sync && id->bNumEndpoints > 1) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"non sync-pipe endpoint but multiple endpoints\n");
|
2004-10-16 22:08:50 +04:00
|
|
|
return USBD_INVAL;
|
|
|
|
}
|
|
|
|
epdesc1 = NULL;
|
|
|
|
if (id->bNumEndpoints > 1) {
|
2005-05-30 08:21:39 +04:00
|
|
|
epdesc1 = (const void*)(tbuf + offs);
|
2004-10-16 22:08:50 +04:00
|
|
|
if (epdesc1->bDescriptorType != UDESC_ENDPOINT)
|
|
|
|
return USBD_INVAL;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("endpoint[1] bLength=%d "
|
2004-10-16 22:08:50 +04:00
|
|
|
"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,
|
2011-11-27 08:10:38 +04:00
|
|
|
epdesc1->bRefresh, epdesc1->bSynchAddress);
|
2004-10-16 22:08:50 +04:00
|
|
|
offs += epdesc1->bLength;
|
|
|
|
if (offs > size)
|
|
|
|
return USBD_INVAL;
|
|
|
|
if (epdesc1->bSynchAddress != 0) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"invalid endpoint: bSynchAddress=0\n");
|
2004-10-16 22:08:50 +04:00
|
|
|
return USBD_INVAL;
|
|
|
|
}
|
|
|
|
if (UE_GET_XFERTYPE(epdesc1->bmAttributes) != UE_ISOCHRONOUS) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"invalid endpoint: bmAttributes=0x%x\n",
|
|
|
|
epdesc1->bmAttributes);
|
2004-10-16 22:08:50 +04:00
|
|
|
return USBD_INVAL;
|
|
|
|
}
|
|
|
|
if (epdesc1->bEndpointAddress != ed->bSynchAddress) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"invalid endpoint addresses: "
|
|
|
|
"ep[0]->bSynchAddress=0x%x "
|
|
|
|
"ep[1]->bEndpointAddress=0x%x\n",
|
|
|
|
ed->bSynchAddress, epdesc1->bEndpointAddress);
|
2004-10-16 22:08:50 +04:00
|
|
|
return USBD_INVAL;
|
|
|
|
}
|
|
|
|
/* UE_GET_ADDR(epdesc1->bEndpointAddress), and epdesc1->bRefresh */
|
|
|
|
}
|
|
|
|
|
1999-10-14 00:13:29 +04:00
|
|
|
format = UGETW(asid->wFormatTag);
|
1999-09-09 16:28:25 +04:00
|
|
|
chan = asf1d->bNrChannels;
|
|
|
|
prec = asf1d->bBitResolution;
|
2002-03-12 18:12:03 +03:00
|
|
|
if (prec != 8 && prec != 16 && prec != 24) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored setting with precision %d\n", prec);
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
switch (format) {
|
1999-10-14 00:13:29 +04:00
|
|
|
case UA_FMT_PCM:
|
2002-03-12 18:12:03 +03:00
|
|
|
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;
|
|
|
|
}
|
1999-10-25 14:16:49 +04:00
|
|
|
enc = AUDIO_ENCODING_SLINEAR_LE;
|
2004-10-02 23:14:54 +04:00
|
|
|
format_str = "pcm";
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
1999-10-14 00:13:29 +04:00
|
|
|
case UA_FMT_PCM8:
|
1999-10-25 14:16:49 +04:00
|
|
|
enc = AUDIO_ENCODING_ULINEAR_LE;
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_altflags |= HAS_8U;
|
2004-10-02 23:14:54 +04:00
|
|
|
format_str = "pcm8";
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
1999-10-14 00:13:29 +04:00
|
|
|
case UA_FMT_ALAW:
|
1999-10-25 14:16:49 +04:00
|
|
|
enc = AUDIO_ENCODING_ALAW;
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_altflags |= HAS_ALAW;
|
2004-10-02 23:14:54 +04:00
|
|
|
format_str = "alaw";
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
1999-10-14 00:13:29 +04:00
|
|
|
case UA_FMT_MULAW:
|
1999-10-25 14:16:49 +04:00
|
|
|
enc = AUDIO_ENCODING_ULAW;
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_altflags |= HAS_MULAW;
|
2004-10-02 23:14:54 +04:00
|
|
|
format_str = "mulaw";
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
2004-10-16 22:08:50 +04:00
|
|
|
case UA_FMT_IEEE_FLOAT:
|
1999-09-09 16:28:25 +04:00
|
|
|
default:
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored setting with format %d\n", format);
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2004-10-02 23:14:54 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug_dev(sc->sc_dev, "%s: %dch, %d/%dbit, %s,",
|
2004-10-02 23:14:54 +04:00
|
|
|
dir == UE_DIR_IN ? "recording" : "playback",
|
|
|
|
chan, prec, asf1d->bSubFrameSize * 8, format_str);
|
|
|
|
if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug(" %d-%dHz\n", UA_SAMP_LO(asf1d),
|
|
|
|
UA_SAMP_HI(asf1d));
|
2004-10-02 23:14:54 +04:00
|
|
|
} else {
|
|
|
|
int r;
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug(" %d", UA_GETSAMP(asf1d, 0));
|
2004-10-02 23:14:54 +04:00
|
|
|
for (r = 1; r < asf1d->bSamFreqType; r++)
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug(",%d", UA_GETSAMP(asf1d, r));
|
|
|
|
aprint_debug("Hz\n");
|
2004-10-02 23:14:54 +04:00
|
|
|
}
|
|
|
|
#endif
|
1999-09-09 16:28:25 +04:00
|
|
|
ai.alt = id->bAlternateSetting;
|
|
|
|
ai.encoding = enc;
|
2002-03-07 17:37:02 +03:00
|
|
|
ai.attributes = sed->bmAttributes;
|
1999-09-09 16:28:25 +04:00
|
|
|
ai.idesc = id;
|
|
|
|
ai.edesc = ed;
|
2004-10-16 22:08:50 +04:00
|
|
|
ai.edesc1 = epdesc1;
|
1999-09-09 16:28:25 +04:00
|
|
|
ai.asf1desc = asf1d;
|
2002-03-07 17:37:02 +03:00
|
|
|
ai.sc_busy = 0;
|
2006-05-11 23:09:25 +04:00
|
|
|
ai.aformat = NULL;
|
|
|
|
ai.ifaceh = NULL;
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_add_alt(sc, &ai);
|
2002-03-07 17:37:02 +03:00
|
|
|
#ifdef UAUDIO_DEBUG
|
2004-10-02 23:14:54 +04:00
|
|
|
if (ai.attributes & UA_SED_FREQ_CONTROL)
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(1, "%s", "FREQ_CONTROL\n");
|
2004-10-02 23:14:54 +04:00
|
|
|
if (ai.attributes & UA_SED_PITCH_CONTROL)
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(1, "%s", "PITCH_CONTROL\n");
|
2002-03-07 17:37:02 +03:00
|
|
|
#endif
|
2002-02-12 22:52:43 +03:00
|
|
|
sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD;
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
#undef offs
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2004-10-02 23:14:54 +04:00
|
|
|
uaudio_identify_as(struct uaudio_softc *sc,
|
|
|
|
const usb_config_descriptor_t *cdesc)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2004-10-02 23:14:54 +04:00
|
|
|
const usb_interface_descriptor_t *id;
|
2005-05-30 08:21:39 +04:00
|
|
|
const char *tbuf;
|
2004-11-13 18:00:48 +03:00
|
|
|
struct audio_format *auf;
|
|
|
|
const struct usb_audio_streaming_type1_descriptor *t1desc;
|
1999-09-09 16:28:25 +04:00
|
|
|
int size, offs;
|
2004-11-13 18:00:48 +03:00
|
|
|
int i, j;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
size = UGETW(cdesc->wTotalLength);
|
2005-05-30 08:21:39 +04:00
|
|
|
tbuf = (const char *)cdesc;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* Locate the AudioStreaming interface descriptor. */
|
|
|
|
offs = 0;
|
2005-05-30 08:21:39 +04:00
|
|
|
id = uaudio_find_iface(tbuf, size, &offs, UISUBCLASS_AUDIOSTREAM);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (id == NULL)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* Loop through all the alternate settings. */
|
|
|
|
while (offs <= size) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "interface=%d offset=%d\n",
|
|
|
|
id->bInterfaceNumber, offs);
|
1999-09-09 16:28:25 +04:00
|
|
|
switch (id->bNumEndpoints) {
|
|
|
|
case 0:
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "AS null alt=%d\n",
|
|
|
|
id->bAlternateSetting);
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_nullalt = id->bAlternateSetting;
|
|
|
|
break;
|
|
|
|
case 1:
|
2004-10-16 22:08:50 +04:00
|
|
|
#ifdef UAUDIO_MULTIPLE_ENDPOINTS
|
|
|
|
case 2:
|
|
|
|
#endif
|
2005-05-30 08:21:39 +04:00
|
|
|
uaudio_process_as(sc, tbuf, &offs, size, id);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"ignored audio interface with %d endpoints\n",
|
|
|
|
id->bNumEndpoints);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
}
|
2005-05-30 08:21:39 +04:00
|
|
|
id = uaudio_find_iface(tbuf, size, &offs,UISUBCLASS_AUDIOSTREAM);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (id == NULL)
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (offs > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%d alts available\n", sc->sc_nalts);
|
2002-02-12 22:52:43 +03:00
|
|
|
|
2004-07-09 06:57:48 +04:00
|
|
|
if (sc->sc_mode == 0) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "no usable endpoint found\n");
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
2002-02-12 22:52:43 +03:00
|
|
|
|
2004-11-13 18:00:48 +03:00
|
|
|
/* build audio_format array */
|
|
|
|
sc->sc_formats = malloc(sizeof(struct audio_format) * sc->sc_nalts,
|
|
|
|
M_USBDEV, M_NOWAIT);
|
|
|
|
if (sc->sc_formats == NULL)
|
|
|
|
return USBD_NOMEM;
|
|
|
|
sc->sc_nformats = sc->sc_nalts;
|
|
|
|
for (i = 0; i < sc->sc_nalts; i++) {
|
|
|
|
auf = &sc->sc_formats[i];
|
|
|
|
t1desc = sc->sc_alts[i].asf1desc;
|
|
|
|
auf->driver_data = NULL;
|
|
|
|
if (UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress) == UE_DIR_OUT)
|
|
|
|
auf->mode = AUMODE_PLAY;
|
|
|
|
else
|
|
|
|
auf->mode = AUMODE_RECORD;
|
|
|
|
auf->encoding = sc->sc_alts[i].encoding;
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
auf->validbits = t1desc->bBitResolution;
|
|
|
|
auf->precision = t1desc->bSubFrameSize * 8;
|
2004-11-13 18:00:48 +03:00
|
|
|
auf->channels = t1desc->bNrChannels;
|
|
|
|
auf->channel_mask = sc->sc_channel_config;
|
|
|
|
auf->frequency_type = t1desc->bSamFreqType;
|
|
|
|
if (t1desc->bSamFreqType == UA_SAMP_CONTNUOUS) {
|
|
|
|
auf->frequency[0] = UA_SAMP_LO(t1desc);
|
|
|
|
auf->frequency[1] = UA_SAMP_HI(t1desc);
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < t1desc->bSamFreqType; j++) {
|
|
|
|
if (j >= AUFMT_MAX_FREQUENCIES) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("%s: please increase "
|
2004-11-13 18:00:48 +03:00
|
|
|
"AUFMT_MAX_FREQUENCIES to %d\n",
|
|
|
|
__func__, t1desc->bSamFreqType);
|
2012-05-18 11:52:54 +04:00
|
|
|
auf->frequency_type =
|
|
|
|
AUFMT_MAX_FREQUENCIES;
|
2012-03-15 22:40:32 +04:00
|
|
|
break;
|
|
|
|
}
|
2004-11-13 18:00:48 +03:00
|
|
|
auf->frequency[j] = UA_GETSAMP(t1desc, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->sc_alts[i].aformat = auf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != auconv_create_encodings(sc->sc_formats, sc->sc_nformats,
|
|
|
|
&sc->sc_encodings)) {
|
|
|
|
free(sc->sc_formats, M_DEVBUF);
|
|
|
|
sc->sc_formats = NULL;
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2004-10-02 23:14:54 +04:00
|
|
|
uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2004-10-21 16:41:07 +04:00
|
|
|
struct io_terminal* iot;
|
2004-10-02 23:14:54 +04:00
|
|
|
const usb_interface_descriptor_t *id;
|
|
|
|
const struct usb_audio_control_descriptor *acdp;
|
2007-02-26 16:14:11 +03:00
|
|
|
const uaudio_cs_descriptor_t *dp;
|
2004-10-21 16:41:07 +04:00
|
|
|
const struct usb_audio_output_terminal *pot;
|
|
|
|
struct terminal_list *tml;
|
2005-05-30 08:21:39 +04:00
|
|
|
const char *tbuf, *ibuf, *ibufend;
|
2010-12-28 23:11:18 +03:00
|
|
|
int size, offs, ndps, i, j;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
size = UGETW(cdesc->wTotalLength);
|
2005-05-30 08:21:39 +04:00
|
|
|
tbuf = (const char *)cdesc;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* Locate the AudioControl interface descriptor. */
|
|
|
|
offs = 0;
|
2005-05-30 08:21:39 +04:00
|
|
|
id = uaudio_find_iface(tbuf, size, &offs, UISUBCLASS_AUDIOCONTROL);
|
1999-12-07 00:06:59 +03:00
|
|
|
if (id == NULL)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (offs + sizeof *acdp > size)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
sc->sc_ac_iface = id->bInterfaceNumber;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"AC interface is %d\n", sc->sc_ac_iface);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* A class-specific AC interface header should follow. */
|
2005-05-30 08:21:39 +04:00
|
|
|
ibuf = tbuf + offs;
|
2010-12-28 23:11:18 +03:00
|
|
|
ibufend = tbuf + size;
|
2004-10-02 23:14:54 +04:00
|
|
|
acdp = (const struct usb_audio_control_descriptor *)ibuf;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (acdp->bDescriptorType != UDESC_CS_INTERFACE ||
|
|
|
|
acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-10-25 14:16:49 +04:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
if (!(usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BAD_ADC) &&
|
|
|
|
UGETW(acdp->bcdADC) != UAUDIO_VERSION)
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-11-02 19:54:27 +03:00
|
|
|
sc->sc_audio_rev = UGETW(acdp->bcdADC);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "found AC header, vers=%03x\n", sc->sc_audio_rev);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
sc->sc_nullalt = -1;
|
|
|
|
|
|
|
|
/* Scan through all the AC specific descriptors */
|
2007-02-26 16:14:11 +03:00
|
|
|
dp = (const uaudio_cs_descriptor_t *)ibuf;
|
1999-09-09 16:28:25 +04:00
|
|
|
ndps = 0;
|
2004-10-21 16:41:07 +04:00
|
|
|
iot = malloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO);
|
|
|
|
if (iot == NULL) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("%s: no memory\n", __func__);
|
2004-10-21 16:41:07 +04:00
|
|
|
return USBD_NOMEM;
|
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
for (;;) {
|
|
|
|
ibuf += dp->bLength;
|
|
|
|
if (ibuf >= ibufend)
|
|
|
|
break;
|
2007-02-26 16:14:11 +03:00
|
|
|
dp = (const uaudio_cs_descriptor_t *)ibuf;
|
2005-02-24 11:19:38 +03:00
|
|
|
if (ibuf + dp->bLength > ibufend) {
|
|
|
|
free(iot, M_TEMP);
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_INVAL;
|
2005-02-24 11:19:38 +03:00
|
|
|
}
|
2010-12-28 23:11:18 +03:00
|
|
|
if (dp->bDescriptorType != UDESC_CS_INTERFACE)
|
|
|
|
break;
|
2004-10-21 16:41:07 +04:00
|
|
|
i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId;
|
|
|
|
iot[i].d.desc = dp;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (i > ndps)
|
|
|
|
ndps = i;
|
|
|
|
}
|
|
|
|
ndps++;
|
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
/* 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 UAUDIO_DEBUG
|
|
|
|
for (i = 0; i < 256; i++) {
|
2004-11-05 20:46:14 +03:00
|
|
|
struct usb_audio_cluster cluster;
|
|
|
|
|
2004-10-21 16:41:07 +04:00
|
|
|
if (iot[i].d.desc == NULL)
|
|
|
|
continue;
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("id %d:\t", i);
|
2004-10-21 16:41:07 +04:00
|
|
|
switch (iot[i].d.desc->bDescriptorSubtype) {
|
|
|
|
case UDESCSUB_AC_INPUT:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_INPUT type=%s\n", uaudio_get_terminal_name
|
2004-10-29 20:42:47 +04:00
|
|
|
(UGETW(iot[i].d.it->wTerminalType)));
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\t");
|
2004-11-05 20:46:14 +03:00
|
|
|
cluster = uaudio_get_cluster(i, iot);
|
|
|
|
uaudio_dump_cluster(&cluster);
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_OUTPUT:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name
|
2004-10-29 20:42:47 +04:00
|
|
|
(UGETW(iot[i].d.ot->wTerminalType)));
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("src=%d\n", iot[i].d.ot->bSourceId);
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_MIXER:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_MIXER src=");
|
2004-10-21 16:41:07 +04:00
|
|
|
for (j = 0; j < iot[i].d.mu->bNrInPins; j++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%d ", iot[i].d.mu->baSourceId[j]);
|
|
|
|
printf("\n\t");
|
2004-11-05 20:46:14 +03:00
|
|
|
cluster = uaudio_get_cluster(i, iot);
|
|
|
|
uaudio_dump_cluster(&cluster);
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_SELECTOR:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_SELECTOR src=");
|
2004-10-21 16:41:07 +04:00
|
|
|
for (j = 0; j < iot[i].d.su->bNrInPins; j++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%d ", iot[i].d.su->baSourceId[j]);
|
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_FEATURE:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_PROCESSING:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_PROCESSING src=");
|
2004-10-21 16:41:07 +04:00
|
|
|
for (j = 0; j < iot[i].d.pu->bNrInPins; j++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%d ", iot[i].d.pu->baSourceId[j]);
|
|
|
|
printf("\n\t");
|
2004-11-05 20:46:14 +03:00
|
|
|
cluster = uaudio_get_cluster(i, iot);
|
|
|
|
uaudio_dump_cluster(&cluster);
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_EXTENSION:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("AC_EXTENSION src=");
|
2004-10-21 16:41:07 +04:00
|
|
|
for (j = 0; j < iot[i].d.eu->bNrInPins; j++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%d ", iot[i].d.eu->baSourceId[j]);
|
|
|
|
printf("\n\t");
|
2004-11-05 20:46:14 +03:00
|
|
|
cluster = uaudio_get_cluster(i, iot);
|
|
|
|
uaudio_dump_cluster(&cluster);
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
break;
|
|
|
|
default:
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("unknown audio control (subtype=%d)\n",
|
2004-10-29 20:42:47 +04:00
|
|
|
iot[i].d.desc->bDescriptorSubtype);
|
2004-10-21 16:41:07 +04:00
|
|
|
}
|
|
|
|
for (j = 0; j < iot[i].inputs_size; j++) {
|
|
|
|
int k;
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\tinput%d: ", j);
|
2004-10-21 16:41:07 +04:00
|
|
|
tml = iot[i].inputs[j];
|
|
|
|
if (tml == NULL) {
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("NULL\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (k = 0; k < tml->size; k++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%s ", uaudio_get_terminal_name
|
2004-10-29 20:42:47 +04:00
|
|
|
(tml->terminals[k]));
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
}
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("\toutput: ");
|
2004-10-21 16:41:07 +04:00
|
|
|
tml = iot[i].output;
|
|
|
|
for (j = 0; j < tml->size; j++)
|
2010-11-04 01:34:23 +03:00
|
|
|
printf("%s ", uaudio_get_terminal_name(tml->terminals[j]));
|
|
|
|
printf("\n");
|
2004-10-21 16:41:07 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
for (i = 0; i < ndps; i++) {
|
2004-10-21 16:41:07 +04:00
|
|
|
dp = iot[i].d.desc;
|
1999-12-07 00:06:59 +03:00
|
|
|
if (dp == NULL)
|
1999-09-09 16:28:25 +04:00
|
|
|
continue;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("id=%d subtype=%d\n", i, dp->bDescriptorSubtype);
|
1999-09-09 16:28:25 +04:00
|
|
|
switch (dp->bDescriptorSubtype) {
|
|
|
|
case UDESCSUB_AC_HEADER:
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_identify_ac: unexpected AC header\n");
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_INPUT:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_input(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_OUTPUT:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_output(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_MIXER:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_mixer(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_SELECTOR:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_selector(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_FEATURE:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_feature(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_PROCESSING:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_processing(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
case UDESCSUB_AC_EXTENSION:
|
2004-10-21 16:41:07 +04:00
|
|
|
uaudio_add_extension(sc, iot, i);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
default:
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error(
|
|
|
|
"uaudio_identify_ac: bad AC desc subtype=0x%02x\n",
|
|
|
|
dp->bDescriptorSubtype);
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-10-21 16:41:07 +04:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct mixerctl *mc;
|
2004-10-03 10:01:09 +04:00
|
|
|
int n, nctls, i;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "index=%d\n", mi->index);
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
n = mi->index;
|
|
|
|
nctls = sc->sc_nctls;
|
|
|
|
|
2003-02-16 21:16:07 +03:00
|
|
|
switch (n) {
|
|
|
|
case UAC_OUTPUT:
|
|
|
|
mi->type = AUDIO_MIXER_CLASS;
|
|
|
|
mi->mixer_class = UAC_OUTPUT;
|
|
|
|
mi->next = mi->prev = AUDIO_MIXER_LAST;
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name));
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
2003-02-16 21:16:07 +03:00
|
|
|
case UAC_INPUT:
|
|
|
|
mi->type = AUDIO_MIXER_CLASS;
|
|
|
|
mi->mixer_class = UAC_INPUT;
|
|
|
|
mi->next = mi->prev = AUDIO_MIXER_LAST;
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name));
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
2003-02-16 21:16:07 +03:00
|
|
|
case UAC_EQUAL:
|
|
|
|
mi->type = AUDIO_MIXER_CLASS;
|
|
|
|
mi->mixer_class = UAC_EQUAL;
|
|
|
|
mi->next = mi->prev = AUDIO_MIXER_LAST;
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->label.name, AudioCequalization,
|
|
|
|
sizeof(mi->label.name));
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
2004-10-21 16:41:07 +04:00
|
|
|
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;
|
2003-02-16 21:16:07 +03:00
|
|
|
default:
|
|
|
|
break;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2003-02-16 21:16:07 +03:00
|
|
|
|
|
|
|
n -= UAC_NCLASSES;
|
|
|
|
if (n < 0 || n >= nctls)
|
2005-01-15 18:19:51 +03:00
|
|
|
return ENXIO;
|
2003-02-16 21:16:07 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
mc = &sc->sc_ctls[n];
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name));
|
1999-09-09 16:28:25 +04:00
|
|
|
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;
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->un.e.member[0].label.name, AudioNoff,
|
|
|
|
sizeof(mi->un.e.member[0].label.name));
|
1999-09-09 16:28:25 +04:00
|
|
|
mi->un.e.member[0].ord = 0;
|
2004-04-23 21:25:25 +04:00
|
|
|
strlcpy(mi->un.e.member[1].label.name, AudioNon,
|
|
|
|
sizeof(mi->un.e.member[1].label.name));
|
1999-09-09 16:28:25 +04:00
|
|
|
mi->un.e.member[1].ord = 1;
|
|
|
|
break;
|
2004-10-03 10:01:09 +04:00
|
|
|
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;
|
1999-09-09 16:28:25 +04:00
|
|
|
default:
|
|
|
|
mi->type = AUDIO_MIXER_VALUE;
|
|
|
|
strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
|
|
|
|
mi->un.v.num_channels = mc->nchan;
|
2000-12-29 16:28:41 +03:00
|
|
|
mi->un.v.delta = mc->delta;
|
1999-09-09 16:28:25 +04:00
|
|
|
break;
|
|
|
|
}
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_open(void *addr, int flags)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("sc=%p\n", sc);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-07-17 02:39:23 +04:00
|
|
|
if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY))
|
2005-01-15 18:19:51 +03:00
|
|
|
return EACCES;
|
2004-07-17 02:39:23 +04:00
|
|
|
if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD))
|
2005-01-15 18:19:51 +03:00
|
|
|
return EACCES;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close function is called at splaudio().
|
|
|
|
*/
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2006-11-16 04:32:37 +03:00
|
|
|
uaudio_close(void *addr)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_drain(void *addr)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-24 03:07:28 +04:00
|
|
|
struct uaudio_softc *sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KASSERT(mutex_owned(&sc->sc_intr_lock));
|
|
|
|
|
|
|
|
kpause("uaudiodr", false,
|
|
|
|
mstohz(UAUDIO_NCHANBUFS * UAUDIO_NFRAMES), &sc->sc_intr_lock);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_halt_out_dma(void *addr)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-24 03:07:28 +04:00
|
|
|
struct uaudio_softc *sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%s", "enter\n");
|
2011-11-24 03:07:28 +04:00
|
|
|
|
|
|
|
KERNEL_LOCK(1, curlwp);
|
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
2002-02-12 22:52:43 +03:00
|
|
|
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);
|
2004-07-09 06:57:48 +04:00
|
|
|
sc->sc_playchan.intr = NULL;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_halt_in_dma(void *addr)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-24 03:07:28 +04:00
|
|
|
struct uaudio_softc *sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%s", "enter\n");
|
2011-11-24 03:07:28 +04:00
|
|
|
|
|
|
|
KERNEL_LOCK(1, curlwp);
|
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
2002-02-12 22:52:43 +03:00
|
|
|
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);
|
2004-07-09 06:57:48 +04:00
|
|
|
sc->sc_recchan.intr = NULL;
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_getdev(void *addr, struct audio_device *retp)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("%s", "\n");
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2010-12-24 23:49:55 +03:00
|
|
|
*retp = sc->sc_adev;
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the block size is large enough to hold all outstanding transfers.
|
|
|
|
*/
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
uaudio_round_blocksize(void *addr, int blk,
|
|
|
|
int mode, const audio_params_t *param)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
2005-01-16 15:46:00 +03:00
|
|
|
int b;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("blk=%d mode=%s\n", blk,
|
|
|
|
mode == AUMODE_PLAY ? "AUMODE_PLAY" : "AUMODE_RECORD");
|
2005-01-16 15:46:00 +03:00
|
|
|
|
|
|
|
/* 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;
|
2002-02-12 22:52:43 +03:00
|
|
|
} else {
|
2005-01-16 15:46:00 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
2002-02-12 22:52:43 +03:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-16 15:46:00 +03:00
|
|
|
if (b <= 0)
|
|
|
|
b = 1;
|
|
|
|
blk = blk <= b ? b : blk / b * b;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-10-25 14:16:49 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (blk <= 0) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_debug("uaudio_round_blocksize: blk=%d\n", blk);
|
1999-10-25 14:16:49 +04:00
|
|
|
blk = 512;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("resultant blk=%d\n", blk);
|
2005-01-15 18:19:51 +03:00
|
|
|
return blk;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2006-11-16 04:32:37 +03:00
|
|
|
uaudio_get_props(void *addr)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
Static void
|
|
|
|
uaudio_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread)
|
|
|
|
{
|
|
|
|
struct uaudio_softc *sc;
|
|
|
|
|
|
|
|
sc = addr;
|
|
|
|
*intr = &sc->sc_intr_lock;
|
|
|
|
*thread = &sc->sc_lock;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-19 15:43:48 +04:00
|
|
|
uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue,
|
2000-06-01 18:28:57 +04:00
|
|
|
int wIndex, int len)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
usb_device_request_t req;
|
|
|
|
u_int8_t data[4];
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-09-09 16:28:25 +04:00
|
|
|
int val;
|
|
|
|
|
|
|
|
if (wValue == -1)
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
req.bmRequestType = type;
|
|
|
|
req.bRequest = which;
|
|
|
|
USETW(req.wValue, wValue);
|
|
|
|
USETW(req.wIndex, wIndex);
|
|
|
|
USETW(req.wLength, len);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"type=0x%02x req=0x%02x wValue=0x%04x "
|
2002-03-23 20:17:10 +03:00
|
|
|
"wIndex=0x%04x len=%d\n",
|
2011-11-27 08:10:38 +04:00
|
|
|
type, which, wValue, wIndex, len);
|
2002-02-12 22:52:43 +03:00
|
|
|
err = usbd_do_request(sc->sc_udev, &req, data);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (err) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("err=%s\n", usbd_errstr(err));
|
2005-01-15 18:19:51 +03:00
|
|
|
return -1;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
switch (len) {
|
|
|
|
case 1:
|
|
|
|
val = data[0];
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val = data[0] | (data[1] << 8);
|
|
|
|
break;
|
|
|
|
default:
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("bad length=%d\n", len);
|
2005-01-15 18:19:51 +03:00
|
|
|
return -1;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"val=%d\n", val);
|
2005-01-15 18:19:51 +03:00
|
|
|
return val;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-19 15:43:48 +04:00
|
|
|
uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue,
|
2000-06-01 18:28:57 +04:00
|
|
|
int wIndex, int len, int val)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
usb_device_request_t req;
|
|
|
|
u_int8_t data[4];
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2,"type=0x%02x req=0x%02x wValue=0x%04x "
|
2002-03-23 20:17:10 +03:00
|
|
|
"wIndex=0x%04x len=%d, val=%d\n",
|
2011-11-27 08:10:38 +04:00
|
|
|
type, which, wValue, wIndex, len, val & 0xffff);
|
2002-02-12 22:52:43 +03:00
|
|
|
err = usbd_do_request(sc->sc_udev, &req, data);
|
1999-10-14 05:18:39 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
1999-11-19 02:32:25 +03:00
|
|
|
if (err)
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("err=%d\n", err);
|
1999-09-09 16:28:25 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_signext(int type, int val)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
if (!MIX_UNSIGNED(type)) {
|
|
|
|
if (MIX_SIZE(type) == 2)
|
|
|
|
val = (int16_t)val;
|
|
|
|
else
|
|
|
|
val = (int8_t)val;
|
|
|
|
}
|
2005-01-15 18:19:51 +03:00
|
|
|
return val;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_value2bsd(struct mixerctl *mc, int val)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "type=%03x val=%d min=%d max=%d ",
|
|
|
|
mc->type, val, mc->minval, mc->maxval);
|
2004-10-03 10:01:09 +04:00
|
|
|
if (mc->type == MIX_ON_OFF) {
|
2002-02-12 22:52:43 +03:00
|
|
|
val = (val != 0);
|
2004-10-03 10:01:09 +04:00
|
|
|
} else if (mc->type == MIX_SELECTOR) {
|
|
|
|
if (val < mc->minval || val > mc->maxval)
|
|
|
|
val = mc->minval;
|
|
|
|
} else
|
2003-10-14 17:12:19 +04:00
|
|
|
val = ((uaudio_signext(mc->type, val) - mc->minval) * 255
|
2000-12-29 17:49:28 +03:00
|
|
|
+ mc->mul/2) / mc->mul;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN_CLEAN(5, "val'=%d\n", val);
|
2005-01-15 18:19:51 +03:00
|
|
|
return val;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_bsd2value(struct mixerctl *mc, int val)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5,"type=%03x val=%d min=%d max=%d ",
|
|
|
|
mc->type, val, mc->minval, mc->maxval);
|
2004-10-03 10:01:09 +04:00
|
|
|
if (mc->type == MIX_ON_OFF) {
|
2002-02-12 22:52:43 +03:00
|
|
|
val = (val != 0);
|
2004-10-03 10:01:09 +04:00
|
|
|
} else if (mc->type == MIX_SELECTOR) {
|
|
|
|
if (val < mc->minval || val > mc->maxval)
|
|
|
|
val = mc->minval;
|
|
|
|
} else
|
2003-10-14 17:12:19 +04:00
|
|
|
val = (val + mc->delta/2) * mc->mul / 255 + mc->minval;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN_CLEAN(5, "val'=%d\n", val);
|
2005-01-15 18:19:51 +03:00
|
|
|
return val;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2002-03-23 20:17:10 +03:00
|
|
|
uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc,
|
2000-06-01 18:28:57 +04:00
|
|
|
int chan)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5,"which=%d chan=%d\n", which, chan);
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
1999-09-09 16:28:25 +04:00
|
|
|
val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan],
|
|
|
|
mc->wIndex, MIX_SIZE(mc->type));
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
2005-01-15 18:19:51 +03:00
|
|
|
return uaudio_value2bsd(mc, val);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc,
|
|
|
|
int chan, int val)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2011-11-27 11:36:54 +04:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
val = uaudio_bsd2value(mc, val);
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan],
|
|
|
|
mc->wIndex, MIX_SIZE(mc->type), val);
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct mixerctl *mc;
|
|
|
|
int i, n, vals[MIX_MAX_CHAN], val;
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "index=%d\n", cp->dev);
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2003-02-16 21:16:07 +03:00
|
|
|
n = cp->dev - UAC_NCLASSES;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (n < 0 || n >= sc->sc_nctls)
|
2005-01-15 18:19:51 +03:00
|
|
|
return ENXIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
mc = &sc->sc_ctls[n];
|
|
|
|
|
|
|
|
if (mc->type == MIX_ON_OFF) {
|
|
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
|
2004-10-03 10:01:09 +04:00
|
|
|
} else if (mc->type == MIX_SELECTOR) {
|
|
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
2004-10-03 10:01:09 +04:00
|
|
|
cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
|
1999-09-09 16:28:25 +04:00
|
|
|
} else {
|
|
|
|
if (cp->type != AUDIO_MIXER_VALUE)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (cp->un.value.num_channels != 1 &&
|
|
|
|
cp->un.value.num_channels != mc->nchan)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
1999-09-09 16:28:25 +04:00
|
|
|
struct mixerctl *mc;
|
|
|
|
int i, n, vals[MIX_MAX_CHAN];
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(2, "index = %d\n", cp->dev);
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2003-02-16 21:16:07 +03:00
|
|
|
n = cp->dev - UAC_NCLASSES;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (n < 0 || n >= sc->sc_nctls)
|
2005-01-15 18:19:51 +03:00
|
|
|
return ENXIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
mc = &sc->sc_ctls[n];
|
|
|
|
|
|
|
|
if (mc->type == MIX_ON_OFF) {
|
|
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
|
2004-10-03 10:01:09 +04:00
|
|
|
} else if (mc->type == MIX_SELECTOR) {
|
|
|
|
if (cp->type != AUDIO_MIXER_ENUM)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
2004-10-03 10:01:09 +04:00
|
|
|
uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
|
1999-09-09 16:28:25 +04:00
|
|
|
} else {
|
|
|
|
if (cp->type != AUDIO_MIXER_VALUE)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
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
|
2005-01-15 18:19:51 +03:00
|
|
|
return EINVAL;
|
1999-09-09 16:28:25 +04:00
|
|
|
for (i = 0; i < mc->nchan; i++)
|
|
|
|
uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]);
|
|
|
|
}
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_trigger_input(void *addr, void *start, void *end, int blksize,
|
|
|
|
void (*intr)(void *), void *arg,
|
2006-11-16 04:32:37 +03:00
|
|
|
const audio_params_t *param)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
|
|
|
struct chan *ch;
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-10-25 14:16:49 +04:00
|
|
|
int i, s;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(3, "sc=%p start=%p end=%p "
|
|
|
|
"blksize=%d\n", sc, start, end, blksize);
|
2005-01-15 18:19:51 +03:00
|
|
|
ch = &sc->sc_recchan;
|
2002-03-18 17:06:08 +03:00
|
|
|
uaudio_chan_set_param(ch, start, end, blksize);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(3, "sample_size=%d bytes/frame=%d "
|
1999-10-25 14:16:49 +04:00
|
|
|
"fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
|
2011-11-27 08:10:38 +04:00
|
|
|
ch->fraction);
|
1999-10-25 14:16:49 +04:00
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_chan_alloc_buffers(sc, ch);
|
2011-11-24 03:07:28 +04:00
|
|
|
if (err) {
|
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2011-11-24 03:07:28 +04:00
|
|
|
}
|
1999-10-25 14:16:49 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_chan_open(sc, ch);
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (err) {
|
1999-10-25 14:16:49 +04:00
|
|
|
uaudio_chan_free_buffers(sc, ch);
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
|
|
|
|
2002-02-12 22:52:43 +03:00
|
|
|
ch->intr = intr;
|
|
|
|
ch->arg = arg;
|
1999-10-25 14:16:49 +04:00
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
2011-11-27 11:36:54 +04:00
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
1999-10-25 14:16:49 +04:00
|
|
|
s = splusb();
|
2000-03-29 05:47:25 +04:00
|
|
|
for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */
|
1999-10-25 14:16:49 +04:00
|
|
|
uaudio_chan_rtransfer(ch);
|
|
|
|
splx(s);
|
2011-11-27 11:36:54 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
1999-10-25 14:16:49 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_trigger_output(void *addr, void *start, void *end, int blksize,
|
|
|
|
void (*intr)(void *), void *arg,
|
2006-11-16 04:32:37 +03:00
|
|
|
const audio_params_t *param)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
|
|
|
struct chan *ch;
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-09-09 16:28:25 +04:00
|
|
|
int i, s;
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(3, "sc=%p start=%p end=%p "
|
|
|
|
"blksize=%d\n", sc, start, end, blksize);
|
2005-01-15 18:19:51 +03:00
|
|
|
ch = &sc->sc_playchan;
|
2002-03-18 17:06:08 +03:00
|
|
|
uaudio_chan_set_param(ch, start, end, blksize);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(3, "sample_size=%d bytes/frame=%d "
|
1999-09-09 16:28:25 +04:00
|
|
|
"fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
|
2011-11-27 08:10:38 +04:00
|
|
|
ch->fraction);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_chan_alloc_buffers(sc, ch);
|
2011-11-24 03:07:28 +04:00
|
|
|
if (err) {
|
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
2011-11-24 03:07:28 +04:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
err = uaudio_chan_open(sc, ch);
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (err) {
|
1999-09-09 16:28:25 +04:00
|
|
|
uaudio_chan_free_buffers(sc, ch);
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2002-02-12 22:52:43 +03:00
|
|
|
ch->intr = intr;
|
|
|
|
ch->arg = arg;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
2011-11-27 08:32:41 +04:00
|
|
|
mutex_spin_exit(&sc->sc_intr_lock);
|
1999-09-09 16:28:25 +04:00
|
|
|
s = splusb();
|
2000-03-29 05:47:25 +04:00
|
|
|
for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */
|
1999-10-25 14:16:49 +04:00
|
|
|
uaudio_chan_ptransfer(ch);
|
1999-09-09 16:28:25 +04:00
|
|
|
splx(s);
|
2011-11-27 08:32:41 +04:00
|
|
|
mutex_spin_enter(&sc->sc_intr_lock);
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up a pipe for a channel. */
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct as_info *as;
|
2012-04-22 05:31:48 +04:00
|
|
|
usb_device_descriptor_t *ddesc;
|
2005-01-15 18:19:51 +03:00
|
|
|
int endpt;
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_status err;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
as = &sc->sc_alts[ch->altidx];
|
|
|
|
endpt = as->edesc->bEndpointAddress;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("endpt=0x%02x, speed=%d, alt=%d\n",
|
|
|
|
endpt, ch->sample_rate, as->alt);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* Set alternate interface corresponding to the mode. */
|
2001-01-04 09:20:49 +03:00
|
|
|
err = usbd_set_interface(as->ifaceh, as->alt);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (err)
|
2005-01-15 18:19:51 +03:00
|
|
|
return err;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-10-29 21:12:53 +04:00
|
|
|
/*
|
|
|
|
* Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
|
|
|
|
*/
|
2012-04-22 05:31:48 +04:00
|
|
|
ddesc = usbd_get_device_descriptor(sc->sc_udev);
|
|
|
|
if ((UGETW(ddesc->idVendor) != USB_VENDOR_ROLAND) &&
|
|
|
|
(UGETW(ddesc->idProduct) != USB_PRODUCT_ROLAND_SD90)) {
|
2004-10-29 21:12:53 +04:00
|
|
|
err = uaudio_set_speed(sc, endpt, ch->sample_rate);
|
2006-10-04 19:53:24 +04:00
|
|
|
if (err) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("set_speed failed err=%s\n", usbd_errstr(err));
|
2006-10-04 19:53:24 +04:00
|
|
|
}
|
2004-10-29 21:12:53 +04:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-10-22 19:25:56 +04:00
|
|
|
ch->pipe = 0;
|
|
|
|
ch->sync_pipe = 0;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("create pipe to 0x%02x\n", endpt);
|
2001-01-04 09:20:49 +03:00
|
|
|
err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe);
|
2004-10-22 19:25:56 +04:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (as->edesc1 != NULL) {
|
|
|
|
endpt = as->edesc1->bEndpointAddress;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("create sync-pipe to 0x%02x\n", endpt);
|
2004-10-22 19:25:56 +04:00
|
|
|
err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe);
|
|
|
|
}
|
|
|
|
return err;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct as_info *as;
|
2001-01-04 09:20:49 +03:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
as = &sc->sc_alts[ch->altidx];
|
2002-04-20 21:04:32 +04:00
|
|
|
as->sc_busy = 0;
|
2004-11-13 18:00:48 +03:00
|
|
|
AUFMT_VALIDATE(as->aformat);
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_nullalt >= 0) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("set null alt=%d\n", sc->sc_nullalt);
|
2001-01-04 09:20:49 +03:00
|
|
|
usbd_set_interface(as->ifaceh, sc->sc_nullalt);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2004-10-22 19:25:56 +04:00
|
|
|
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);
|
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
1999-11-12 03:34:57 +03:00
|
|
|
usbd_xfer_handle xfer;
|
2005-05-30 08:21:39 +04:00
|
|
|
void *tbuf;
|
1999-09-09 16:28:25 +04:00
|
|
|
int i, size;
|
|
|
|
|
|
|
|
size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES;
|
|
|
|
for (i = 0; i < UAUDIO_NCHANBUFS; i++) {
|
1999-11-18 02:00:49 +03:00
|
|
|
xfer = usbd_alloc_xfer(sc->sc_udev);
|
1999-11-12 03:34:57 +03:00
|
|
|
if (xfer == 0)
|
1999-09-09 16:28:25 +04:00
|
|
|
goto bad;
|
1999-11-12 03:34:57 +03:00
|
|
|
ch->chanbufs[i].xfer = xfer;
|
2005-05-30 08:21:39 +04:00
|
|
|
tbuf = usbd_alloc_buffer(xfer, size);
|
|
|
|
if (tbuf == 0) {
|
1999-09-09 16:28:25 +04:00
|
|
|
i++;
|
|
|
|
goto bad;
|
|
|
|
}
|
2005-05-30 08:21:39 +04:00
|
|
|
ch->chanbufs[i].buffer = tbuf;
|
1999-09-09 16:28:25 +04:00
|
|
|
ch->chanbufs[i].chan = ch;
|
|
|
|
}
|
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NORMAL_COMPLETION;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
bad:
|
|
|
|
while (--i >= 0)
|
|
|
|
/* implicit buffer free */
|
1999-11-18 02:00:49 +03:00
|
|
|
usbd_free_xfer(ch->chanbufs[i].xfer);
|
2005-01-15 18:19:51 +03:00
|
|
|
return USBD_NOMEM;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2006-11-16 04:32:37 +03:00
|
|
|
uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < UAUDIO_NCHANBUFS; i++)
|
1999-11-18 02:00:49 +03:00
|
|
|
usbd_free_xfer(ch->chanbufs[i].xfer);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Called at splusb() */
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_ptransfer(struct chan *ch)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
struct chanbuf *cb;
|
|
|
|
int i, n, size, residue, total;
|
|
|
|
|
1999-11-09 19:52:14 +03:00
|
|
|
if (ch->sc->sc_dying)
|
|
|
|
return;
|
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
/* 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) {
|
2002-02-12 22:52:43 +03:00
|
|
|
if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
|
2000-12-28 14:56:22 +03:00
|
|
|
size += ch->sample_size;
|
1999-09-09 16:28:25 +04:00
|
|
|
residue -= USB_FRAMES_PER_SECOND;
|
|
|
|
}
|
|
|
|
cb->sizes[i] = size;
|
|
|
|
total += size;
|
|
|
|
}
|
|
|
|
ch->residue = residue;
|
|
|
|
cb->size = total;
|
|
|
|
|
2002-03-23 20:17:10 +03:00
|
|
|
/*
|
1999-09-09 16:28:25 +04:00
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
1999-10-14 05:18:39 +04:00
|
|
|
#ifdef UAUDIO_DEBUG
|
1999-09-09 16:28:25 +04:00
|
|
|
if (uaudiodebug > 8) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue);
|
1999-09-09 16:28:25 +04:00
|
|
|
for (i = 0; i < UAUDIO_NFRAMES; i++) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF(" [%d] length %d\n", i, cb->sizes[i]);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-27 11:36:54 +04:00
|
|
|
//DPRINTFN(5, "ptransfer xfer=%p\n", cb->xfer);
|
1999-09-09 16:28:25 +04:00
|
|
|
/* Fill the request */
|
2002-03-23 20:17:10 +03:00
|
|
|
usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
|
|
|
|
UAUDIO_NFRAMES, USBD_NO_COPY,
|
1999-11-18 02:00:49 +03:00
|
|
|
uaudio_chan_pintr);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
(void)usbd_transfer(cb->xfer);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv,
|
|
|
|
usbd_status status)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct chanbuf *cb;
|
|
|
|
struct chan *ch;
|
|
|
|
uint32_t count;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
cb = priv;
|
|
|
|
ch = cb->chan;
|
1999-09-09 16:28:25 +04:00
|
|
|
/* Return if we are aborting. */
|
|
|
|
if (status == USBD_CANCELLED)
|
|
|
|
return;
|
|
|
|
|
2000-02-08 21:45:27 +03:00
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "count=%d, transferred=%d\n",
|
|
|
|
count, ch->transferred);
|
1999-09-09 16:28:25 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (count != cb->size) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_chan_pintr: count(%d) != size(%d)\n",
|
1999-09-09 16:28:25 +04:00
|
|
|
count, cb->size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-10-25 14:16:49 +04:00
|
|
|
ch->transferred += cb->size;
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&ch->sc->sc_intr_lock);
|
1999-10-25 14:16:49 +04:00
|
|
|
/* Call back to upper layer */
|
|
|
|
while (ch->transferred >= ch->blksize) {
|
|
|
|
ch->transferred -= ch->blksize;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg);
|
1999-10-25 14:16:49 +04:00
|
|
|
ch->intr(ch->arg);
|
|
|
|
}
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_exit(&ch->sc->sc_intr_lock);
|
1999-10-25 14:16:49 +04:00
|
|
|
|
|
|
|
/* start next transfer */
|
|
|
|
uaudio_chan_ptransfer(ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called at splusb() */
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_rtransfer(struct chan *ch)
|
1999-10-25 14:16:49 +04:00
|
|
|
{
|
|
|
|
struct chanbuf *cb;
|
|
|
|
int i, size, residue, total;
|
|
|
|
|
1999-11-09 19:52:14 +03:00
|
|
|
if (ch->sc->sc_dying)
|
|
|
|
return;
|
|
|
|
|
1999-10-25 14:16:49 +04:00
|
|
|
/* 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;
|
2002-12-02 05:36:14 +03:00
|
|
|
cb->offsets[i] = total;
|
1999-10-25 14:16:49 +04:00
|
|
|
total += size;
|
|
|
|
}
|
|
|
|
ch->residue = residue;
|
|
|
|
cb->size = total;
|
|
|
|
|
|
|
|
#ifdef UAUDIO_DEBUG
|
|
|
|
if (uaudiodebug > 8) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue);
|
1999-10-25 14:16:49 +04:00
|
|
|
for (i = 0; i < UAUDIO_NFRAMES; i++) {
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF(" [%d] length %d\n", i, cb->sizes[i]);
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "transfer xfer=%p\n", cb->xfer);
|
1999-10-25 14:16:49 +04:00
|
|
|
/* Fill the request */
|
2002-03-23 20:17:10 +03:00
|
|
|
usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
|
|
|
|
UAUDIO_NFRAMES, USBD_NO_COPY,
|
1999-11-18 02:00:49 +03:00
|
|
|
uaudio_chan_rintr);
|
1999-10-25 14:16:49 +04:00
|
|
|
|
1999-11-12 03:34:57 +03:00
|
|
|
(void)usbd_transfer(cb->xfer);
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv,
|
|
|
|
usbd_status status)
|
1999-10-25 14:16:49 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct chanbuf *cb;
|
|
|
|
struct chan *ch;
|
|
|
|
uint32_t count;
|
2011-11-24 03:07:28 +04:00
|
|
|
int i, n, frsize;
|
1999-10-25 14:16:49 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
cb = priv;
|
|
|
|
ch = cb->chan;
|
1999-10-25 14:16:49 +04:00
|
|
|
/* Return if we are aborting. */
|
|
|
|
if (status == USBD_CANCELLED)
|
|
|
|
return;
|
|
|
|
|
2000-02-08 21:45:27 +03:00
|
|
|
usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "count=%d, transferred=%d\n", count, ch->transferred);
|
2000-12-28 14:56:22 +03:00
|
|
|
|
2002-12-02 05:36:14 +03:00
|
|
|
/* count < cb->size is normal for asynchronous source */
|
1999-10-25 14:16:49 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2002-12-02 05:36:14 +03:00
|
|
|
if (count > cb->size) {
|
2008-05-24 20:40:58 +04:00
|
|
|
aprint_error("uaudio_chan_rintr: count(%d) > size(%d)\n",
|
1999-10-25 14:16:49 +04:00
|
|
|
count, cb->size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-03-23 20:17:10 +03:00
|
|
|
/*
|
1999-10-25 14:16:49 +04:00
|
|
|
* Transfer data from channel buffer to upper layer buffer, taking
|
|
|
|
* care of wrapping the upper layer buffer.
|
|
|
|
*/
|
2002-12-02 05:36:14 +03:00
|
|
|
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;
|
|
|
|
}
|
1999-10-25 14:16:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call back to upper layer */
|
2002-12-02 05:36:14 +03:00
|
|
|
ch->transferred += count;
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_enter(&ch->sc->sc_intr_lock);
|
1999-09-09 16:28:25 +04:00
|
|
|
while (ch->transferred >= ch->blksize) {
|
|
|
|
ch->transferred -= ch->blksize;
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg);
|
1999-09-09 16:28:25 +04:00
|
|
|
ch->intr(ch->arg);
|
|
|
|
}
|
2011-11-24 03:07:28 +04:00
|
|
|
mutex_spin_exit(&ch->sc->sc_intr_lock);
|
1999-09-09 16:28:25 +04:00
|
|
|
|
|
|
|
/* start next transfer */
|
1999-10-25 14:16:49 +04:00
|
|
|
uaudio_chan_rtransfer(ch);
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2003-03-30 08:47:13 +04:00
|
|
|
uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param,
|
|
|
|
int maxpktsize)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
int samples_per_frame, sample_size;
|
|
|
|
|
2002-03-18 17:06:08 +03:00
|
|
|
ch->altidx = altidx;
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
sample_size = param->precision * param->channels / 8;
|
|
|
|
samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND;
|
1999-09-09 16:28:25 +04:00
|
|
|
ch->sample_size = sample_size;
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
ch->sample_rate = param->sample_rate;
|
2003-03-30 08:47:13 +04:00
|
|
|
if (maxpktsize == 0) {
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND;
|
2003-03-30 08:47:13 +04:00
|
|
|
ch->bytes_per_frame = samples_per_frame * sample_size;
|
|
|
|
} else {
|
|
|
|
ch->fraction = 0;
|
|
|
|
ch->bytes_per_frame = maxpktsize;
|
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
ch->residue = 0;
|
2002-03-18 17:06:08 +03:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static void
|
2002-03-18 17:06:08 +03:00
|
|
|
uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize)
|
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
|
1999-09-09 16:28:25 +04:00
|
|
|
ch->start = start;
|
|
|
|
ch->end = end;
|
|
|
|
ch->cur = start;
|
|
|
|
ch->blksize = blksize;
|
|
|
|
ch->transferred = 0;
|
|
|
|
ch->curchanbuf = 0;
|
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static int
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_set_params(void *addr, int setmode, int usemode,
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
struct audio_params *play, struct audio_params *rec,
|
|
|
|
stream_filter_list_t *pfil, stream_filter_list_t *rfil)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
2005-01-15 18:19:51 +03:00
|
|
|
struct uaudio_softc *sc;
|
|
|
|
int paltidx, raltidx;
|
2001-01-04 10:05:00 +03:00
|
|
|
struct audio_params *p;
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
stream_filter_list_t *fil;
|
2004-11-13 18:00:48 +03:00
|
|
|
int mode, i;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
sc = addr;
|
|
|
|
paltidx = -1;
|
|
|
|
raltidx = -1;
|
1999-09-09 16:28:25 +04:00
|
|
|
if (sc->sc_dying)
|
2005-01-15 18:19:51 +03:00
|
|
|
return EIO;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-07-08 02:04:28 +04:00
|
|
|
if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) ||
|
|
|
|
((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL))
|
2005-01-15 18:19:51 +03:00
|
|
|
return EBUSY;
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-11-13 18:00:48 +03:00
|
|
|
if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) {
|
2002-02-12 22:52:43 +03:00
|
|
|
sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0;
|
2004-11-13 18:00:48 +03:00
|
|
|
AUFMT_VALIDATE(sc->sc_alts[sc->sc_playchan.altidx].aformat);
|
|
|
|
}
|
|
|
|
if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) {
|
2002-02-12 22:52:43 +03:00
|
|
|
sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0;
|
2004-11-13 18:00:48 +03:00
|
|
|
AUFMT_VALIDATE(sc->sc_alts[sc->sc_recchan.altidx].aformat);
|
|
|
|
}
|
2002-02-12 22:52:43 +03:00
|
|
|
|
2004-07-17 00:08:23 +04:00
|
|
|
/* Some uaudio devices are unidirectional. Don't try to find a
|
|
|
|
matching mode for the unsupported direction. */
|
|
|
|
setmode &= sc->sc_mode;
|
|
|
|
|
2001-01-04 10:05:00 +03:00
|
|
|
for (mode = AUMODE_RECORD; mode != -1;
|
|
|
|
mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
|
2004-07-17 00:08:23 +04:00
|
|
|
if ((setmode & mode) == 0)
|
2001-01-04 10:05:00 +03:00
|
|
|
continue;
|
2002-03-23 20:17:10 +03:00
|
|
|
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
if (mode == AUMODE_PLAY) {
|
|
|
|
p = play;
|
|
|
|
fil = pfil;
|
|
|
|
} else {
|
|
|
|
p = rec;
|
|
|
|
fil = rfil;
|
|
|
|
}
|
2004-11-13 18:00:48 +03:00
|
|
|
i = auconv_set_converter(sc->sc_formats, sc->sc_nformats,
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
mode, p, TRUE, fil);
|
2002-03-07 17:37:02 +03:00
|
|
|
if (i < 0)
|
2004-11-13 18:00:48 +03:00
|
|
|
return EINVAL;
|
2004-07-08 02:04:28 +04:00
|
|
|
|
|
|
|
if (mode == AUMODE_PLAY)
|
|
|
|
paltidx = i;
|
|
|
|
else
|
|
|
|
raltidx = i;
|
2002-02-12 22:52:43 +03:00
|
|
|
}
|
|
|
|
|
2004-07-08 02:04:28 +04:00
|
|
|
if ((setmode & AUMODE_PLAY)) {
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
p = pfil->req_size > 0 ? &pfil->filters[0].param : play;
|
2002-02-12 22:52:43 +03:00
|
|
|
/* XXX abort transfer if currently happening? */
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
uaudio_chan_init(&sc->sc_playchan, paltidx, p, 0);
|
2002-02-12 22:52:43 +03:00
|
|
|
}
|
2004-07-08 02:04:28 +04:00
|
|
|
if ((setmode & AUMODE_RECORD)) {
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
p = rfil->req_size > 0 ? &pfil->filters[0].param : rec;
|
2002-02-12 22:52:43 +03:00
|
|
|
/* XXX abort transfer if currently happening? */
|
merge kent-audio1 branch, which introduces audio filter pipeline to the MI
audio framework
Summary of changes:
* struct audio_params
- remove sw_code, factor, factor_denom, hw_sample_rate,
hw_encoding ,hw_precision, and hw_channels. Conversion information
is conveyed by stream_filter_list_t.
- change the type of sample_rate: u_long -> u_int
- add `validbits,' which represents the valid data size in
precision bits. It is required in order to distinguish 24/32bit
from 24/24bit or 32/32bit.
* audio_hw_if
- add two parameters to set_params()
stream_filter_list_t *pfil, stream_filter_list *rfil
A HW driver should set filter recipes for requested formats
- constify audio_params parameters of trigger_output() and
trigger_input(). They represent audio formats for the hardware.
- make open() and close() optional
- add int (AUMODE_PLAY or AUMODE_RECORD) and audio_params_t parameters
to round_blocksize()
* sw_code is replaced with stream_filter_t.
stream_filer_t converts audio data in an input buffer and writes
into another output buffer unlike sw_code, which converts data in
single buffer.
converters in dev/auconv.c, dev/mulaw.c, dev/aurateconv.c,
dev/tc/bba.c, dev/ic/msm6258.c, and arch/arm/iomd/vidcaudio.c are
reimplemented as stream_filter_t
* MI audio
- audiosetinfo() builds filter pipelines from stream_filter_list_t
filled by audio_hw_if::set_params()
- audiosetinfo() returns with EINVAL if mmapped and set_params()
requests filters
- audio_write(), audio_pint(), and audio_rint() invoke a filter
pipeline.
- ioctl() for FIONREAD, AUDIO_WSEEK, AUDIO_GETIOFFS,
AUDIO_GETOOFFS, and audio_prinfo::{seek,samples} for
AUDIO_GETINFO handle values for a buffer nearest to userland.
* add `struct device *' parameter to ac97_attach()
* all of audio HW drivers follow audio_hw_if and ac97 changes
2005-01-11 01:01:36 +03:00
|
|
|
uaudio_chan_init(&sc->sc_recchan, raltidx, p,
|
2003-03-30 08:47:13 +04:00
|
|
|
UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize));
|
2001-01-04 10:05:00 +03:00
|
|
|
}
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2004-11-13 18:00:48 +03:00
|
|
|
if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) {
|
2004-07-08 02:04:28 +04:00
|
|
|
sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1;
|
2004-11-13 18:00:48 +03:00
|
|
|
AUFMT_INVALIDATE(sc->sc_alts[sc->sc_playchan.altidx].aformat);
|
|
|
|
}
|
|
|
|
if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) {
|
2004-07-08 02:04:28 +04:00
|
|
|
sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1;
|
2004-11-13 18:00:48 +03:00
|
|
|
AUFMT_INVALIDATE(sc->sc_alts[sc->sc_recchan.altidx].aformat);
|
|
|
|
}
|
2004-07-08 02:04:28 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTF("use altidx=p%d/r%d, altno=p%d/r%d\n",
|
2002-03-23 20:17:10 +03:00
|
|
|
sc->sc_playchan.altidx, sc->sc_recchan.altidx,
|
2002-02-12 22:52:43 +03:00
|
|
|
(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
|
2011-11-27 08:10:38 +04:00
|
|
|
: -1);
|
2002-03-23 20:17:10 +03:00
|
|
|
|
2005-01-15 18:19:51 +03:00
|
|
|
return 0;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
|
|
|
|
2004-10-22 20:01:49 +04:00
|
|
|
Static usbd_status
|
2000-06-01 18:28:57 +04:00
|
|
|
uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed)
|
1999-09-09 16:28:25 +04:00
|
|
|
{
|
|
|
|
usb_device_request_t req;
|
2011-11-24 03:07:28 +04:00
|
|
|
usbd_status err;
|
2005-01-15 18:19:51 +03:00
|
|
|
uint8_t data[3];
|
1999-09-09 16:28:25 +04:00
|
|
|
|
2011-11-27 08:10:38 +04:00
|
|
|
DPRINTFN(5, "endpt=%d speed=%u\n", endpt, speed);
|
1999-09-09 16:28:25 +04:00
|
|
|
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;
|
|
|
|
|
2011-11-24 03:07:28 +04:00
|
|
|
KERNEL_LOCK(1, curlwp);
|
|
|
|
err = usbd_do_request(sc->sc_udev, &req, data);
|
|
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
|
|
|
|
|
|
|
return err;
|
1999-09-09 16:28:25 +04:00
|
|
|
}
|
2008-06-28 13:14:56 +04:00
|
|
|
|
|
|
|
#ifdef _MODULE
|
|
|
|
|
|
|
|
MODULE(MODULE_CLASS_DRIVER, uaudio, NULL);
|
|
|
|
|
|
|
|
static const struct cfiattrdata audiobuscf_iattrdata = {
|
|
|
|
"audiobus", 0, { { NULL, NULL, 0 }, }
|
|
|
|
};
|
|
|
|
static const struct cfiattrdata * const uaudio_attrs[] = {
|
|
|
|
&audiobuscf_iattrdata, NULL
|
|
|
|
};
|
|
|
|
CFDRIVER_DECL(uaudio, DV_DULL, uaudio_attrs);
|
|
|
|
extern struct cfattach uaudio_ca;
|
|
|
|
static int uaudioloc[6/*USBIFIFCF_NLOCS*/] = {
|
|
|
|
-1/*USBIFIFCF_PORT_DEFAULT*/,
|
|
|
|
-1/*USBIFIFCF_CONFIGURATION_DEFAULT*/,
|
|
|
|
-1/*USBIFIFCF_INTERFACE_DEFAULT*/,
|
|
|
|
-1/*USBIFIFCF_VENDOR_DEFAULT*/,
|
|
|
|
-1/*USBIFIFCF_PRODUCT_DEFAULT*/,
|
|
|
|
-1/*USBIFIFCF_RELEASE_DEFAULT*/};
|
|
|
|
static struct cfparent uhubparent = {
|
|
|
|
"usbifif", NULL, DVUNIT_ANY
|
|
|
|
};
|
|
|
|
static struct cfdata uaudio_cfdata[] = {
|
|
|
|
{
|
|
|
|
.cf_name = "uaudio",
|
|
|
|
.cf_atname = "uaudio",
|
|
|
|
.cf_unit = 0,
|
|
|
|
.cf_fstate = FSTATE_STAR,
|
|
|
|
.cf_loc = uaudioloc,
|
|
|
|
.cf_flags = 0,
|
|
|
|
.cf_pspec = &uhubparent,
|
|
|
|
},
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
uaudio_modcmd(modcmd_t cmd, void *arg)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case MODULE_CMD_INIT:
|
|
|
|
err = config_cfdriver_attach(&uaudio_cd);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
err = config_cfattach_attach("uaudio", &uaudio_ca);
|
|
|
|
if (err) {
|
|
|
|
config_cfdriver_detach(&uaudio_cd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
err = config_cfdata_attach(uaudio_cfdata, 1);
|
|
|
|
if (err) {
|
|
|
|
config_cfattach_detach("uaudio", &uaudio_ca);
|
|
|
|
config_cfdriver_detach(&uaudio_cd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case MODULE_CMD_FINI:
|
|
|
|
err = config_cfdata_detach(uaudio_cfdata);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
config_cfattach_detach("uaudio", &uaudio_ca);
|
|
|
|
config_cfdriver_detach(&uaudio_cd);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return ENOTTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|