summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_imux/oss_imux.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_imux/oss_imux.c')
-rw-r--r--kernel/drv/oss_imux/oss_imux.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/kernel/drv/oss_imux/oss_imux.c b/kernel/drv/oss_imux/oss_imux.c
new file mode 100644
index 0000000..e712436
--- /dev/null
+++ b/kernel/drv/oss_imux/oss_imux.c
@@ -0,0 +1,1072 @@
+/*
+ * Purpose: Pseudo driver for sharing one input device between multiple apps.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#define MAX_IMUX_INSTANCES 16
+#define MAX_IMUX_DEV 48
+#define SUPPORTED_FORMATS (AFMT_S16_NE|AFMT_S16_OE)
+
+#include "oss_imux_cfg.h"
+
+static unsigned long long used_masterdevs = 0LL;
+
+typedef struct
+{
+ int audio_dev;
+ int port_number;
+ int is_opened, is_prepared, is_triggered;
+ int speed, channels, fmt;
+ int ch_index;
+ int left_igain, right_igain;
+ oss_peaks_t peaks;
+}
+imux_portc;
+
+typedef struct
+{
+ oss_device_t *osdev;
+ oss_device_t *master_osdev;
+ oss_mutex_t mutex;
+ int installed_ok;
+ int hw_dev;
+ int hw_speed, hw_channels, hw_fmt, sw_fmt;
+ int hw_fragsize;
+
+ int device_started;
+ struct fileinfo finfo;
+
+ int nr_devices, open_count;
+ imux_portc portc[MAX_IMUX_DEV];
+ int fragsize;
+ int prev_fragment;
+
+/*
+ * Mixer
+ */
+ int mixer_dev;
+ int autoreset; /* Autoreset igain sliders to 100 during open */
+
+/*
+ * Startup info
+ */
+ int imux_devices;
+ int imux_rate;
+ int imux_masterdev;
+ int instance_no;
+}
+imux_devc;
+
+static imux_devc imux_info[MAX_IMUX_INSTANCES] = { {0} };
+static int nimuxdevs = 0;
+
+/*ARGSUSED*/
+static int
+imux_set_rate (int dev, int arg)
+{
+ imux_devc *devc = audio_engines[dev]->devc;
+ return devc->hw_speed;
+}
+
+/*ARGSUSED*/
+static short
+imux_set_channels (int dev, short arg)
+{
+ imux_portc *portc = audio_engines[dev]->portc;
+ return portc->channels = 2;
+}
+
+/*ARGSUSED*/
+static unsigned int
+imux_set_format (int dev, unsigned int arg)
+{
+ imux_devc *devc = audio_engines[dev]->devc;
+
+ return devc->sw_fmt;
+}
+
+
+static int
+imux_igain (int dev, int ctrl, unsigned int cmd, int value)
+{
+/*
+ * Access function for IMUX input gain
+ */
+ int left, right;
+ imux_devc *devc = mixer_devs[dev]->devc;
+ imux_portc *portc;
+
+ if (ctrl != 100 && (ctrl < 0 || ctrl >= devc->imux_devices))
+ return OSS_EINVAL;
+
+ portc = &devc->portc[ctrl];
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ if (ctrl == 100)
+ return devc->autoreset;
+ return (portc->left_igain & 0x00ff) |
+ ((portc->right_igain & 0x00ff) << 8);
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ if (ctrl == 100)
+ return devc->autoreset = !!value;
+
+ left = value & 0x00ff;
+ right = (value >> 8) & 0x00ff;
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ portc->left_igain = left;
+ portc->right_igain = right;
+ return left | (right << 8);
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+imux_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ imux_devc *devc = audio_engines[dev]->devc;
+ imux_portc *portc = audio_engines[dev]->portc;
+ int value, i, n, p;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SET_RECSRC:
+ {
+ value = *arg;
+ if (value <= 0 || value >= devc->hw_channels - 1)
+ return OSS_EINVAL;
+ portc->ch_index = value;
+ }
+
+ case SNDCTL_DSP_GET_RECSRC:
+ return *arg = portc->ch_index;
+ break;
+
+ case SNDCTL_DSP_GET_RECSRC_NAMES:
+ {
+ oss_mixer_enuminfo *ei = (oss_mixer_enuminfo *) arg;
+
+ memset (ei, 0, sizeof (*ei));
+
+ n = ei->nvalues = devc->hw_channels - 1;
+ p = 0;
+
+ if (n <= 1) /* Only one alternative */
+ {
+ ei->nvalues = 1;
+ ei->strindex[0] = 0;
+ sprintf (ei->strings, "default");
+
+ }
+ else
+ /* Multiple alternatives */
+ for (i = 0; i < n; i++)
+ {
+ ei->strindex[i] = p;
+
+ sprintf (&ei->strings[p], "CH%d/%d", i + 1, i + 2);
+
+ p += strlen (&ei->strings[p]) + 1;
+ }
+
+ return 0;
+ }
+ break;
+
+ case SNDCTL_DSP_SETRECVOL:
+ value = *arg;
+ return *arg = imux_igain (audio_engines[dev]->mixer_dev,
+ portc->ch_index, SNDCTL_MIX_WRITE, value);
+ break;
+
+ case SNDCTL_DSP_GETRECVOL:
+ return *arg = imux_igain (audio_engines[dev]->mixer_dev,
+ portc->ch_index, SNDCTL_MIX_READ, 0);
+ break;
+
+ case SNDCTL_DSP_GETIPEAKS:
+ memset (arg, 0, sizeof (oss_peaks_t));
+ memcpy (arg, portc->peaks, sizeof (portc->peaks));
+ memset (portc->peaks, 0, sizeof (portc->peaks));
+ return 0;
+ break;
+ }
+ return OSS_EINVAL;
+}
+
+static void imux_trigger (int dev, int state);
+
+static void
+imux_reset (int dev)
+{
+ imux_trigger (dev, 0);
+}
+
+/*ARGSUSED*/
+static void
+bufcpy16ne (imux_devc * devc, imux_portc * portc, unsigned char *tbuf,
+ unsigned char *buf, int nbytes, int frame_size, int hw_framesize)
+{
+ int ns, i, hw_channels;
+ short *p1, *p2;
+
+ ns = nbytes / frame_size;
+
+ hw_channels = hw_framesize / sizeof (*p2);
+
+ p1 = (short *) tbuf;
+ p2 = (short *) (buf + portc->ch_index * frame_size / 2);
+
+ for (i = 0; i < ns; i++)
+ {
+ int v;
+
+ /* Left channel */
+ v = *p2++;
+ v = v * portc->left_igain / 100;
+ *p1++ = v;
+ if (v < 0)
+ v = -v;
+ if (v > portc->peaks[0])
+ portc->peaks[0] = v;
+
+ /* Right channel */
+ v = *p2++;
+ v = v * portc->right_igain / 100;
+ *p1++ = v;
+ if (v < 0)
+ v = -v;
+ if (v > portc->peaks[1])
+ portc->peaks[1] = v;
+
+ if (hw_channels > 2)
+ p2 += hw_channels - 2;
+ }
+}
+
+/*ARGSUSED*/
+static void
+bufcpy16oe (imux_devc * devc, imux_portc * portc, unsigned char *tbuf,
+ unsigned char *buf, int nbytes, int frame_size, int hw_framesize)
+{
+ int ns, i, hw_channels;
+ short *p1, *p2;
+
+ ns = nbytes / frame_size;
+
+ hw_channels = hw_framesize / sizeof (*p2);
+
+ p1 = (short *) tbuf;
+ p2 = (short *) (buf + portc->ch_index * frame_size / 2);
+
+ for (i = 0; i < ns; i++)
+ {
+ int v;
+
+ /* Left channel */
+ v = *p2++;
+ v = ((v >> 8) & 0xff) | ((v & 0xff) << 8);
+ v = v * portc->left_igain / 100;
+ *p1++ = v;
+ if (v < 0)
+ v = -v;
+ if (v > portc->peaks[0])
+ portc->peaks[0] = v;
+
+ /* Right channel */
+ v = *p2++;
+ v = ((v >> 8) & 0xff) | ((v & 0xff) << 8);
+ v = v * portc->right_igain / 100;
+ *p1++ = v;
+ if (v < 0)
+ v = -v;
+ if (v > portc->peaks[1])
+ portc->peaks[1] = v;
+
+ if (hw_channels > 2)
+ p2 += hw_channels - 2;
+ }
+}
+
+/*ARGSUSED*/
+static void
+imux_callback (int dev, int parm)
+{
+ dmap_t *dmap;
+ unsigned char *buf, *tbuf;
+ int i, len, frag, tail, next, n;
+ imux_devc *devc = NULL;
+
+ for (i = 0; i < nimuxdevs && devc == NULL; i++)
+ {
+ if (imux_info[i].hw_dev == dev)
+ devc = &imux_info[i];
+ }
+
+ if (devc == NULL)
+ {
+ cmn_err (CE_WARN, "IMUX error\n");
+ return;
+ }
+
+ dmap = audio_engines[dev]->dmap_in;
+
+ frag = dmap_get_qhead (dmap);
+ tail = dmap_get_qtail (dmap);
+ next = (frag + 1) % dmap->nfrags;
+
+ n = 0;
+ while (n++ < 2 && frag != tail && next != tail
+ && devc->prev_fragment != frag)
+ {
+ devc->prev_fragment = frag;
+ buf = &dmap->dmabuf[frag * dmap->fragment_size];
+ len = dmap->fragment_size;
+
+ dmap->user_counter += dmap->fragment_size;
+
+ for (i = 0; i < devc->nr_devices; i++)
+ {
+ dmap_t *client_dmap;
+
+ imux_portc *portc = &devc->portc[i];
+ client_dmap = audio_engines[portc->audio_dev]->dmap_in;
+
+ if (!portc->is_opened || !portc->is_triggered)
+ continue;
+
+ if (client_dmap->fragment_size != (2 * len) / devc->hw_channels)
+ {
+ DDB (cmn_err (CE_WARN, "Fragment warning (%d, %d, %d-%d)\n",
+ client_dmap->fragment_size, len,
+ audio_engines[portc->audio_dev]->min_block,
+ audio_engines[portc->audio_dev]->max_block));
+#if 1
+ /* Make automatic adjustments */
+ client_dmap->fragment_size = (2 * len) / devc->hw_channels;
+ client_dmap->nfrags =
+ client_dmap->buffsize / client_dmap->fragment_size;
+ client_dmap->bytes_in_use =
+ client_dmap->nfrags * client_dmap->fragment_size;
+#else
+ continue;
+#endif
+ }
+
+ tbuf =
+ &client_dmap->dmabuf[dmap_get_qtail (client_dmap) *
+ client_dmap->fragment_size];
+
+ if (devc->hw_fmt == AFMT_S16_NE) /* Native 16 bits */
+ {
+ bufcpy16ne (devc, portc, tbuf, buf,
+ client_dmap->fragment_size,
+ client_dmap->frame_size, dmap->frame_size);
+ }
+ else if (devc->hw_fmt == AFMT_S16_OE) /* Native 16 bits */
+ {
+ bufcpy16oe (devc, portc, tbuf, buf,
+ client_dmap->fragment_size,
+ client_dmap->frame_size, dmap->frame_size);
+ }
+ else
+ memcpy (tbuf, buf, len);
+#ifdef DO_TIMINGS
+ oss_timing_printf ("Imux: copy f=%d/%d to ch=%d t=%d/%d", frag, tail,
+ i, dmap_get_qtail (client_dmap),
+ dmap_get_qhead (client_dmap));
+#endif
+ oss_audio_inputintr (portc->audio_dev, 0);
+
+ }
+ frag = dmap_get_qhead (dmap);
+ tail = dmap_get_qtail (dmap);
+ next = (frag + 1) % dmap->nfrags;
+ }
+
+
+}
+
+static int
+start_device (imux_devc * devc)
+{
+ int i, err, dev, trig, nc;
+ /* int frags = 0x7fff0008; fragment size of 256 bytes */
+
+ if (devc->hw_dev < 0 || devc->hw_dev >= num_audio_engines)
+ {
+ devc->hw_dev = 0;
+ }
+
+ if (devc->hw_dev < 0 || devc->hw_dev >= num_audio_engines)
+ {
+ cmn_err (CE_WARN, "No audio hardware available\n");
+ return OSS_ENXIO;
+ }
+
+ if (devc->device_started)
+ {
+ return 1;
+ }
+
+ devc->finfo.mode = OPEN_READ;
+ devc->finfo.acc_flags = 0;
+ devc->prev_fragment = -1;
+
+ DDB (cmn_err (CE_NOTE,
+ "Instance %d: Will use audio device %d as the master\n",
+ devc->instance_no, devc->hw_dev));
+
+ if (devc->hw_dev >= devc->portc[0].audio_dev
+ && devc->hw_dev <= devc->portc[devc->imux_devices - 1].audio_dev)
+ {
+ cmn_err (CE_WARN, "Bad master device %d\n", devc->hw_dev);
+ return OSS_ENXIO;
+ }
+
+ if (!(audio_engines[devc->hw_dev]->iformat_mask & SUPPORTED_FORMATS))
+ {
+ cmn_err (CE_CONT,
+ "Audio device %d doesn't support compatible sample formats.\n",
+ devc->hw_dev);
+ return OSS_EIO;
+ }
+ if ((err =
+ oss_audio_open_engine (devc->hw_dev, OSS_DEV_DSP, &devc->finfo, 1, 0,
+ NULL)) < 0)
+ {
+ return err;
+ }
+ audio_engines[devc->hw_dev]->cooked_enable = 0;
+ strcpy (audio_engines[devc->hw_dev]->cmd, "IMUX");
+ audio_engines[devc->hw_dev]->pid = 0;
+
+ devc->hw_fmt =
+ oss_audio_set_format (devc->hw_dev, devc->hw_fmt,
+ audio_engines[devc->hw_dev]->iformat_mask);
+ devc->device_started = 1;
+ devc->hw_channels = 2;
+
+ devc->hw_channels =
+ oss_audio_set_channels (devc->hw_dev, devc->hw_channels);
+
+/*
+ * TODO: Turn off OPT_SHADOW flag from the virtual devices if the master device
+ * works in multi channel mode.
+ */
+ devc->hw_speed = oss_audio_set_rate (devc->hw_dev, devc->hw_speed);
+
+#if 0
+ oss_audio_ioctl (devc->hw_dev, NULL, SNDCTL_DSP_SETFRAGMENT,
+ (ioctl_arg) & frags);
+#endif
+ oss_audio_ioctl (devc->hw_dev, NULL, SNDCTL_DSP_GETBLKSIZE,
+ (ioctl_arg) & devc->fragsize);
+
+ if (!(devc->hw_fmt & SUPPORTED_FORMATS))
+ {
+ oss_audio_release (devc->hw_dev, &devc->finfo);
+ cmn_err (CE_WARN,
+ "This device doesn't support any known sample formats (%x)\n",
+ devc->hw_fmt);
+ return OSS_EIO;
+ }
+
+ switch (devc->hw_fmt)
+ {
+ case AFMT_S16_NE:
+ devc->sw_fmt = AFMT_S16_NE;
+ break;
+
+ case AFMT_S16_OE:
+ devc->sw_fmt = AFMT_S16_NE;
+ break;
+
+ default:
+ cmn_err (CE_WARN, "Bad sample format %x\n", devc->hw_fmt);
+ }
+
+ nc = devc->hw_channels;
+ if (nc < 2)
+ {
+ oss_audio_release (devc->hw_dev, &devc->finfo);
+ cmn_err (CE_WARN, "A 2 channel soundcard (or better) is required\n");
+ return OSS_EIO;
+ }
+
+ DDB (cmn_err (CE_CONT, "Started audio device %d, s=%d, c=%d, bits=%d\n",
+ devc->hw_dev, devc->hw_speed, devc->hw_channels,
+ devc->hw_fmt));
+
+ trig = 0;
+ oss_audio_ioctl (devc->hw_dev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig);
+ trig = PCM_ENABLE_INPUT;
+ oss_audio_ioctl (devc->hw_dev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig);
+
+ devc->hw_fragsize = audio_engines[devc->hw_dev]->dmap_out->fragment_size;
+
+ for (i = 0; i < devc->nr_devices; i++)
+ {
+ int bz = (2 * devc->hw_fragsize) / devc->hw_channels;
+ dev = devc->portc[i].audio_dev;
+ audio_engines[dev]->fixed_rate = devc->hw_speed;
+ audio_engines[dev]->min_block = bz;
+ audio_engines[dev]->max_block = bz;
+ }
+ audio_engines[devc->hw_dev]->dmap_in->audio_callback = imux_callback;
+
+ return 1;
+}
+
+static void
+stop_device (imux_devc * devc)
+{
+ if (!devc->device_started)
+ return;
+
+ oss_audio_ioctl (devc->hw_dev, NULL, SNDCTL_DSP_HALT, 0);
+ oss_audio_release (devc->hw_dev, &devc->finfo);
+ audio_engines[devc->hw_dev]->pid = -1;
+ memset (audio_engines[devc->hw_dev]->cmd, 0,
+ sizeof (audio_engines[devc->hw_dev]->cmd));
+ audio_engines[devc->hw_dev]->flags &= ~ADEV_NOSRC;
+
+ devc->device_started = 0;
+}
+
+/*ARGSUSED*/
+static int
+imux_open (int dev, int mode, int open_flags)
+{
+ imux_portc *portc = audio_engines[dev]->portc;
+ imux_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int bz, err;
+
+ if (devc->hw_dev < 0)
+ {
+ cmn_err (CE_NOTE, "No master device allocated\n");
+ return OSS_EIO;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->is_opened)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->is_opened = 1;
+ portc->is_prepared = 0;
+ portc->is_triggered = 0;
+ portc->channels = 2;
+ portc->ch_index = 0;
+ memset (portc->peaks, 0, sizeof (portc->peaks));
+ portc->fmt = AFMT_S16_NE;
+ audio_engines[dev]->rate_source = audio_engines[devc->hw_dev]->rate_source;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ if (!devc->device_started)
+ if ((err = start_device (devc)) < 0)
+ {
+ portc->is_opened = 0;
+ return err;
+ }
+
+ bz = (2 * devc->hw_fragsize) / devc->hw_channels;
+ audio_engines[dev]->min_block = audio_engines[dev]->max_block = bz;
+ portc->ch_index = portc->port_number % (devc->hw_channels / 2);
+ portc->speed = devc->hw_speed;
+
+ if (devc->autoreset)
+ {
+ portc->left_igain = 100;
+ portc->right_igain = 100;
+ mixer_devs[devc->mixer_dev]->modify_counter++;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_count++;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+imux_close (int dev, int mode)
+{
+ imux_portc *portc = audio_engines[dev]->portc;
+ imux_devc *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int count;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->is_triggered = 0;
+ count = --devc->open_count;
+ /* MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); */
+
+ if (count == 0) /* Last one? */
+ stop_device (devc);
+
+ /* MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); */
+ portc->is_opened = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static void
+imux_output_block (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+/*ARGSUSED*/
+static void
+imux_start_input (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+static void
+imux_trigger (int dev, int state)
+{
+ imux_portc *portc = audio_engines[dev]->portc;
+
+ if (state & PCM_ENABLE_INPUT)
+ portc->is_triggered = 1;
+ else
+ portc->is_triggered = 0;
+}
+
+/*ARGSUSED*/
+static int
+imux_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+imux_prepare_for_output (int dev, int bsize, int bcount)
+{
+ return OSS_EIO;
+}
+
+/*ARGSUSED*/
+static int
+imux_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+#define MY_BUFFSIZE (64*1024)
+ if (dmap->dmabuf != NULL)
+ return 0;
+ dmap->dmabuf_phys = 0; /* Not mmap() capable */
+ dmap->dmabuf = KERNEL_MALLOC (MY_BUFFSIZE);
+ if (dmap->dmabuf == NULL)
+ return OSS_ENOSPC;
+ dmap->buffsize = MY_BUFFSIZE;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+imux_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ if (dmap->dmabuf == NULL)
+ return 0;
+ KERNEL_FREE (dmap->dmabuf);
+
+ dmap->dmabuf = NULL;
+ return 0;
+}
+
+#if 0
+static int
+imux_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+}
+#endif
+
+static audiodrv_t imux_driver = {
+ imux_open,
+ imux_close,
+ imux_output_block,
+ imux_start_input,
+ imux_ioctl,
+ imux_prepare_for_input,
+ imux_prepare_for_output,
+ imux_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ imux_trigger,
+ imux_set_rate,
+ imux_set_format,
+ imux_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ imux_alloc_buffer,
+ imux_free_buffer,
+ NULL,
+ NULL,
+ NULL /* imux_get_buffer_pointer */
+};
+
+/*ARGSUSED*/
+static int
+imux_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static mixer_driver_t imux_mixer_driver = {
+ imux_mixer_ioctl
+};
+
+static const unsigned char peak_cnv[256] = {
+ 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72,
+ 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90,
+ 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101,
+ 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108,
+ 108,
+ 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114,
+ 114,
+ 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119,
+ 119,
+ 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123,
+ 123,
+ 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126,
+ 126,
+ 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129,
+ 130,
+ 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132,
+ 132,
+ 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135,
+ 135,
+ 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137,
+ 137,
+ 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139,
+ 139,
+ 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141,
+ 141,
+ 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143,
+ 143,
+ 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144,
+};
+
+/*ARGSUSED*/
+static int
+imux_vu (int dev, int ctrl, unsigned int cmd, int value)
+{
+/*
+ * Access function for PCM VU meters
+ */
+ int left, right;
+ imux_devc *devc = mixer_devs[dev]->devc;
+
+ if (ctrl < 0 || ctrl >= devc->imux_devices)
+ return OSS_EINVAL;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ imux_portc *portc = &devc->portc[ctrl];
+ left = peak_cnv[(portc->peaks[0] * 144) / 32768];
+ right = peak_cnv[(portc->peaks[1] * 144) / 32768];
+ memset (portc->peaks, 0, sizeof (portc->peaks));
+ return left | (right << 8);
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+imux_mix_init (int dev)
+{
+ int group;
+ imux_devc *devc = mixer_devs[dev]->devc;
+ int i;
+ int err;
+
+ if ((group = mixer_ext_create_group (dev, 0, "CLIENT")) < 0)
+ return group;
+
+ for (i = 0; i < devc->imux_devices; i++)
+ {
+ char tmp[32];
+
+ sprintf (tmp, "@pcm%d", devc->portc[i].audio_dev);
+
+ if ((err = mixer_ext_create_control (dev, group, i, imux_igain,
+ MIXT_STEREOSLIDER,
+ tmp, 100,
+ MIXF_READABLE | MIXF_WRITEABLE)) <
+ 0)
+ return err;
+
+ if ((err = mixer_ext_create_control (dev, group, i, imux_vu,
+ MIXT_STEREOPEAK,
+ "-", 144, MIXF_READABLE)) < 0)
+ return err;
+ }
+
+ if ((err = mixer_ext_create_control (dev, group, 100, imux_igain,
+ MIXT_ONOFF,
+ "autoreset", 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return err;
+ return 0;
+}
+
+static int
+find_master_device (imux_devc * devc)
+{
+/*
+ * Find a suitable master device.
+ *
+ * Return 1 if found and 0 if not.
+ */
+ int dev;
+ adev_p adev;
+
+ if (num_audio_devfiles < 1)
+ return 0;
+
+ if (devc->imux_masterdev >= 0)
+ {
+/*
+ * imux_masterdev property was given. Use the given /dev/dsp# number
+ * as the master device.
+ */
+ if (devc->imux_masterdev >= num_audio_devfiles)
+ return 0; /* Device not attached yet */
+ devc->hw_dev = audio_devfiles[devc->imux_masterdev]->engine_num;
+ adev = audio_engines[devc->hw_dev];
+
+ if (adev->flags & (ADEV_NOINPUT))
+ {
+ cmn_err (CE_NOTE,
+ "Audio device %d cannot be used as an IMUX master device\n",
+ devc->imux_masterdev);
+ devc->hw_dev = -1;
+ return 0;
+ }
+
+ devc->hw_dev = adev->engine_num;
+ /* TODO: Prevent the other virtual drivers from picking this one */
+ }
+ else
+ {
+/*
+ * Try to find if some of the devices currently available is suitable
+ * master device.
+ */
+ devc->hw_dev = -1;
+
+ for (dev = 0; dev < num_audio_engines; dev++)
+ {
+ adev = audio_engines[dev];
+
+ if (adev->flags & ADEV_NOINPUT)
+ continue;
+
+ if (used_masterdevs & (1LL << dev))
+ continue;
+
+ devc->hw_dev = adev->engine_num;
+ break;
+ }
+ }
+
+ return (devc->hw_dev >= 0);
+}
+
+static int
+try_to_start (void *dc)
+{
+ int n, adev, opts;
+ char tmp[32];
+ imux_devc *devc = dc;
+ int my_mixer;
+
+ if (!find_master_device (devc))
+ return 0;
+
+/*
+ * If the situation is hopeless then just tell the caller to stop
+ * trying this operation.
+ */
+ if (devc->hw_dev < 0)
+ return 1;
+
+ if (used_masterdevs & (1LL << devc->hw_dev))
+ {
+ devc->hw_dev = -1;
+ cmn_err (CE_WARN,
+ "Selected master device % for IMUX instance %d already used for some other instance\n",
+ devc->osdev->instance, devc->hw_dev);
+ return 1;
+ }
+
+ used_masterdevs |= (1LL << devc->hw_dev);
+
+ opts =
+ ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL | ADEV_NOOUTPUT |
+ ADEV_FIXEDRATE | ADEV_NOMMAP;
+
+ devc->nr_devices = 0;
+ devc->open_count = 0;
+
+ devc->hw_speed = devc->imux_rate;
+ devc->master_osdev = audio_engines[devc->hw_dev]->osdev;
+ devc->installed_ok = 1;
+ devc->autoreset = 1;
+ MUTEX_INIT (devc->master_osdev, devc->mutex, MH_DRV + 4);
+
+ if ((my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->master_osdev,
+ "IMUX Control panel",
+ &imux_mixer_driver,
+ sizeof (mixer_driver_t), devc)) >= 0)
+ {
+ mixer_ext_set_init_fn (my_mixer, imux_mix_init, 32);
+ }
+ else
+ my_mixer = -1;
+
+ devc->mixer_dev = my_mixer;
+
+ for (n = 0; n < devc->imux_devices; n++)
+ {
+ imux_portc *portc;
+
+ if (n > 0)
+ opts |= ADEV_SHADOW;
+
+ sprintf (tmp, "IMux%d audio record", devc->instance_no);
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->master_osdev,
+ tmp,
+ &imux_driver,
+ sizeof (audiodrv_t),
+ opts, AFMT_S16_NE, devc, -1)) < 0)
+ {
+ return 1;
+ }
+
+ portc = &devc->portc[n];
+
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->mixer_dev = my_mixer;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = 192000;
+ portc->left_igain = 100;
+ portc->right_igain = 100;
+
+ portc->audio_dev = adev;
+ portc->is_opened = 0;
+ portc->is_prepared = 0;
+ portc->is_triggered = 0;
+ portc->speed = 48000;
+ portc->channels = 2;
+ portc->fmt = AFMT_S16_NE;
+ portc->port_number = n;
+
+ devc->nr_devices = n + 1;
+ }
+
+ return 1;
+}
+
+
+int
+oss_imux_attach (oss_device_t * osdev)
+{
+ extern int imux_devices;
+ extern int imux_masterdev;
+ extern int imux_rate;
+ imux_devc *devc;
+
+ if (imux_devices < 2)
+ imux_devices = 2;
+ if (imux_devices > MAX_IMUX_DEV)
+ imux_devices = MAX_IMUX_DEV;
+
+ if (nimuxdevs >= MAX_IMUX_INSTANCES)
+ {
+ cmn_err (CE_NOTE, "Only %d instances permitted\n", MAX_IMUX_INSTANCES);
+ return 0;
+ }
+
+ devc = &imux_info[nimuxdevs++];
+
+ devc->osdev = osdev;
+ osdev->devc = devc;
+
+ devc->master_osdev = NULL;
+ devc->instance_no = osdev->instance;
+ devc->imux_devices = imux_devices;
+ devc->imux_rate = imux_rate;
+ devc->imux_masterdev = imux_masterdev;
+ imux_masterdev = -1;
+ devc->hw_dev = -1;
+ devc->sw_fmt = AFMT_S16_NE;
+
+ oss_register_device (osdev, "OSS IMUX driver");
+
+ if (!try_to_start (devc))
+ {
+ oss_audio_register_client (try_to_start, devc, devc->osdev);
+ }
+
+ return 1;
+}
+
+int
+oss_imux_detach (oss_device_t * osdev)
+{
+ imux_devc *devc = osdev->devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ if (devc->installed_ok)
+ {
+ MUTEX_CLEANUP (devc->mutex);
+ }
+
+ oss_unregister_device (devc->osdev);
+
+ return 1;
+}