summaryrefslogtreecommitdiff
path: root/kernel/framework/remux
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/remux')
-rw-r--r--kernel/framework/remux/.config1
-rw-r--r--kernel/framework/remux/oss_remux.c544
2 files changed, 545 insertions, 0 deletions
diff --git a/kernel/framework/remux/.config b/kernel/framework/remux/.config
new file mode 100644
index 0000000..46b59d2
--- /dev/null
+++ b/kernel/framework/remux/.config
@@ -0,0 +1 @@
+endian=LITTLE
diff --git a/kernel/framework/remux/oss_remux.c b/kernel/framework/remux/oss_remux.c
new file mode 100644
index 0000000..30dca71
--- /dev/null
+++ b/kernel/framework/remux/oss_remux.c
@@ -0,0 +1,544 @@
+/*
+ * Purpose: Multi channel playback support for devices with multiple stereo engines.
+ *
+ * Some sound cards don't provide single multi channel output engine. Instead
+ * they have multiple stereo output pairs connected to the front, side,
+ * center/LFE and rear speakers. The remux driver is used by such drivers to
+ * redistribute multi channel streams to the individual stereo engines.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "oss_config.h"
+
+#ifndef USE_REMUX
+#error remux.c is not compatible with this architecture (endianess)
+#endif
+
+#ifdef OSS_BIG_ENDIAN
+void
+remux_install (char *name, oss_device_t * osdev, int frontdev, int reardev,
+ int center_lfe_dev, int surrounddev)
+{
+ /* Not compatible with big endian yet */
+}
+
+#else
+
+#include "remux.h"
+
+static char *chnames[MAX_SUBDEVS] = {
+ "Front",
+ "Surr",
+ "C&L",
+ "Rear"
+};
+
+static const int bindings[MAX_SUBDEVS] = {
+ DSP_BIND_FRONT,
+ DSP_BIND_SURR,
+ DSP_BIND_CENTER_LFE,
+ DSP_BIND_REAR
+};
+
+static remux_devc dev_info = { 0 }, *devc = &dev_info;
+
+/*
+ * Audio routines
+ */
+/*ARGSUSED*/
+static void
+remux_callback (int dev, int parm)
+{
+ oss_audio_outputintr (devc->audio_dev, 1);
+}
+
+static int
+remux_set_rate (int dev, int arg)
+{
+ remux_devc *devc = audio_engines[dev]->devc;
+
+ return devc->speed =
+ audio_engines[devc->physdev[0]]->d->adrv_set_rate (devc->physdev[0], arg);
+}
+
+static short
+remux_set_channels (int dev, short arg)
+{
+ remux_devc *devc = audio_engines[dev]->devc;
+
+ if (arg == 0)
+ return devc->channels;
+
+ arg &= ~1; /* Make sure we have even number of channels */
+
+ if (arg < 2)
+ return devc->channels = 2;
+ if (arg > devc->maxchannels)
+ return devc->channels = devc->maxchannels;
+
+ return devc->channels = arg;
+}
+
+/*ARGSUSED*/
+static unsigned int
+remux_set_format (int dev, unsigned int arg)
+{
+ return AFMT_S16_NE;
+}
+
+/*ARGSUSED*/
+static int
+remux_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static void remux_trigger (int dev, int state);
+
+static void
+remux_reset (int dev)
+{
+ remux_trigger (dev, 0);
+}
+
+/*ARGSUSED*/
+static int
+remux_open (int dev, int mode, int open_flags)
+{
+ remux_devc *devc = audio_engines[dev]->devc;
+ adev_p adev = audio_engines[devc->audio_dev];
+ oss_native_word flags;
+ int i, j, err;
+
+ if (mode & OPEN_READ)
+ {
+ cmn_err (CE_WARN, "Audio device %d cannot do recording\n", dev);
+ /* return OSS_ENOTSUP; */
+ }
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (devc->open_mode != 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return OSS_EBUSY;
+ }
+ devc->open_mode = mode;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ for (i = 0; i < devc->n_physdevs; i++)
+ {
+ adev_p pdev = audio_engines[devc->physdev[i]];
+
+ devc->finfo[i].mode = OPEN_WRITE;
+ devc->finfo[i].acc_flags = 0;
+ if ((err =
+ oss_audio_open_engine (devc->physdev[i], OSS_DEV_DSP,
+ &devc->finfo[i], 1, OF_SMALLFRAGS,
+ NULL)) < 0)
+ {
+ for (j = 0; j < i; j++)
+ {
+ oss_audio_release (devc->physdev[j], &devc->finfo[j]);
+ }
+
+ devc->open_mode = 0;
+ return err;
+ }
+
+
+ strcpy (pdev->cmd, chnames[i]);
+ pdev->pid = 0;
+ pdev->cooked_enable = 0;
+
+ if (pdev->d->adrv_bind != NULL)
+ {
+ int b = bindings[i];
+ pdev->d->adrv_bind (pdev->engine_num, SNDCTL_DSP_BIND_CHANNEL,
+ (ioctl_arg) & b);
+ }
+
+ if (pdev->flags & ADEV_FIXEDRATE)
+ {
+ adev->flags |= ADEV_FIXEDRATE;
+ adev->fixed_rate = pdev->fixed_rate;
+ }
+ else
+ adev->flags &= ~(ADEV_FIXEDRATE);
+
+ }
+
+ devc->speed = 48000;
+ devc->channels = 2;
+ adev->cooked_enable = 0;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+remux_close (int dev, int mode)
+{
+ int i;
+ remux_devc *devc = audio_engines[dev]->devc;
+
+ for (i = 0; i < devc->n_physdevs; i++)
+ oss_audio_release (devc->physdev[i], &devc->finfo[i]);
+ devc->open_mode = 0;
+}
+
+#if 0
+static int sinebuf[48] = {
+
+ 0, 4276, 8480, 12539, 16383, 19947, 23169, 25995,
+ 28377, 30272, 31650, 32486, 32767, 32486, 31650, 30272,
+ 28377, 25995, 23169, 19947, 16383, 12539, 8480, 4276,
+ 0, -4276, -8480, -12539, -16383, -19947, -23169, -25995,
+ -28377, -30272, -31650, -32486, -32767, -32486, -31650, -30272,
+ -28377, -25995, -23169, -19947, -16383, -12539, -8480, -4276
+};
+#endif
+
+/*ARGSUSED*/
+static void
+remux_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+/*
+ * This routine does the actual de-interleaving of stereo pairs to the
+ * individual devices. All data movement is done based on sample pairs
+ * 2*16=32 bit (2*short=int). Native endianess (AFMT_S16_NE) is assumed.
+ */
+ adev_p adev = audio_engines[dev], pdev = audio_engines[devc->physdev[0]];
+ remux_devc *devc = adev->devc;
+ dmap_p dmap = adev->dmap_out, pdmap = pdev->dmap_out;
+
+ int ch, nc, nf = pdmap->nfrags;
+
+ int *inbuf;
+ int ptr, pos = 0;
+
+ ptr = ((unsigned long) dmap->byte_counter) % dmap->bytes_in_use;
+
+ inbuf = (int *) (dmap->dmabuf + ptr);
+
+ nc = devc->channels / 2;
+
+ for (ch = 0; ch < nc; ch++)
+ {
+ int *outbuf;
+ int i, ns;
+
+ pdev = audio_engines[devc->physdev[ch]];
+ pdmap = pdev->dmap_out;
+ if (ch == 0)
+ pos =
+ (pdmap->user_counter + pdmap->fragment_size) % pdmap->bytes_in_use;
+
+ outbuf = (int *) (pdmap->dmabuf + pos);
+
+ ns = dmap->fragment_size / (4 * nc);
+
+ for (i = 0; i < ns; i++)
+ {
+#if 0
+ static int p[MAX_SUBDEVS] = { 0 };
+
+ short *s = (short *) &outbuf[i];
+ s[0] = s[1] = sinebuf[p[ch]];
+ p[ch] = (p[ch] + 1) % 48;
+#else
+ outbuf[i] = inbuf[(i * nc) + ch];
+#endif
+ }
+
+ pdmap->user_counter += ns * 4;
+ }
+
+ if (pdmap->user_counter - pdmap->byte_counter <
+ pdmap->fragment_size * (nf / 2))
+ {
+ oss_audio_outputintr (devc->audio_dev, 1);
+ }
+}
+
+/*ARGSUSED*/
+static void
+remux_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+}
+
+static void
+remux_trigger (int dev, int state)
+{
+ remux_devc *devc = audio_engines[dev]->devc;
+
+ int i;
+
+ for (i = 0; i < devc->n_physdevs; i++)
+ {
+ int pd = devc->physdev[i];
+ adev_p pdev = audio_engines[pd];
+
+ pdev->d->adrv_trigger (pd, state);
+ if (state & PCM_ENABLE_OUTPUT)
+ pdev->dmap_out->flags |= DMAP_STARTED;
+ else
+ pdev->dmap_out->flags &= ~DMAP_STARTED;
+ }
+}
+
+/*ARGSUSED*/
+static int
+remux_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return OSS_EIO;
+}
+
+static int
+remux_prepare_for_output (int dev, int bsize, int bcount)
+{
+ remux_devc *devc = audio_engines[dev]->devc;
+ adev_p adev = audio_engines[dev];
+ int i, err, tmp, nd;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ nd = devc->channels / 2;
+ bsize = 240; /* Must be divisible by 3*4 and 4*4 */
+
+ for (i = 0; i < devc->n_physdevs; i++)
+ {
+ int pd = devc->physdev[i];
+ adev_p pdev = audio_engines[pd];
+ dmap_p dmap = pdev->dmap_out;
+
+ tmp = pdev->d->adrv_set_format (pd, AFMT_S16_NE);
+ if (tmp != AFMT_S16_NE)
+ {
+ cmn_err (CE_NOTE, "remux: Bad sample format %x\n", tmp);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EIO;
+ }
+
+ tmp = pdev->d->adrv_set_channels (pd, 2);
+ if (tmp != 2)
+ {
+ cmn_err (CE_NOTE, "remux: Bad number of channels %d\n", tmp);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EIO;
+ }
+
+ tmp = pdev->d->adrv_set_rate (pd, devc->speed);
+ if (tmp != devc->speed)
+ {
+ cmn_err (CE_NOTE, "remux: Bad sample rate %d\n", tmp);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EIO;
+ }
+
+ if (pdev->min_block > 0 && bsize < pdev->min_block)
+ bsize = pdev->min_block;
+ if (pdev->max_block > 0 && bsize > pdev->max_block)
+ bsize = pdev->max_block;
+
+ dmap->fragment_size = bsize;
+
+ bcount = dmap->buffsize / bsize;
+
+ dmap->bytes_in_use = bcount * bsize;
+ dmap->nfrags = bcount;
+
+ if ((err = pdev->d->adrv_prepare_for_output (pd, bsize, bcount)) < 0)
+ {
+ cmn_err (CE_WARN,
+ "remux: Preparing device #%d failed, error %d\n", pd, err);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return err;
+ }
+
+ if (dmap->dmabuf == NULL)
+ {
+ cmn_err (CE_WARN, "dmabuf==NULL\n");
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_ENOMEM;
+ }
+
+ memset (dmap->dmabuf, 0, dmap->buffsize);
+
+ if (i == 0)
+ dmap->audio_callback = remux_callback;
+ dmap->dma_mode = PCM_ENABLE_OUTPUT;
+ dmap->flags |= DMAP_PREPARED;
+ dmap->data_rate = devc->speed * 4;
+ }
+
+ adev->dmap_out->fragment_size = bsize * nd;
+ adev->dmap_out->nfrags =
+ adev->dmap_out->bytes_in_use / adev->dmap_out->fragment_size;
+ adev->dmap_out->nfrags &= ~1;
+
+ if (adev->dmap_out->nfrags < 2)
+ adev->dmap_out->nfrags = 2;
+ adev->dmap_out->bytes_in_use =
+ adev->dmap_out->fragment_size * adev->dmap_out->nfrags;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static audiodrv_t remux_driver = {
+ remux_open,
+ remux_close,
+ remux_output_block,
+ remux_start_input,
+ remux_ioctl,
+ remux_prepare_for_input,
+ remux_prepare_for_output,
+ remux_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ remux_trigger,
+ remux_set_rate,
+ remux_set_format,
+ remux_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* remux_alloc_buffer */
+ NULL, /* remux_free_buffer */
+ NULL,
+ NULL,
+ NULL
+};
+
+static int
+check_physdev (int dev)
+{
+ adev_p adev = audio_engines[dev];
+
+ if (!(adev->oformat_mask & AFMT_S16_NE))
+ {
+ cmn_err (CE_NOTE,
+ "remux: Audio device %d doesn't support the 16 bit format\n",
+ dev);
+ return 0;
+ }
+
+ if (adev->flags & (ADEV_NOOUTPUT))
+ {
+ cmn_err (CE_NOTE, "remux: Audio device %d doesn't support output\n",
+ dev);
+ return 0;
+ }
+
+#if 0
+ if (!(adev->flags & ADEV_AUTOMODE))
+ {
+ cmn_err (CE_NOTE, "remux: Audio device %d doesn't support auto-mode\n",
+ dev);
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+static int
+countdevs (int frontdev, int reardev, int center_lfe_dev, int surrounddev)
+{
+ if (frontdev < 0 || frontdev >= num_audio_engines)
+ return 0;
+ if (reardev < 0 || reardev >= num_audio_engines)
+ return 2;
+ if (center_lfe_dev < 0 || center_lfe_dev >= num_audio_engines)
+ return 4;
+ if (surrounddev < 0 || surrounddev >= num_audio_engines)
+ return 6;
+
+ return 8;
+}
+
+void
+remux_install (char *name, oss_device_t * osdev, int frontdev, int reardev,
+ int center_lfe_dev, int surrounddev)
+{
+ adev_p adev, pdev;
+ int n, i;
+
+ DDB (cmn_err (CE_CONT, "remux install %s: %d, %d, %d, %d\n",
+ name, frontdev, reardev, center_lfe_dev, surrounddev));
+
+ devc->osdev = osdev;
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV + 2);
+
+ n = countdevs (frontdev, reardev, center_lfe_dev, surrounddev);
+
+ if (n < 4)
+ {
+ cmn_err (CE_WARN, "remux: Bad devices (%d, %d, %d, %d)\n",
+ frontdev, reardev, center_lfe_dev, surrounddev);
+ return;
+ }
+
+ devc->maxchannels = n;
+ devc->n_physdevs = n / 2;
+ devc->physdev[0] = frontdev;
+ devc->physdev[1] = reardev;
+ devc->physdev[2] = center_lfe_dev;
+ devc->physdev[3] = surrounddev;
+
+ for (i = 0; i < devc->n_physdevs; i++)
+ if (!check_physdev (devc->physdev[i]))
+ return;
+
+ pdev = audio_engines[frontdev];
+
+ if ((devc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ name,
+ &remux_driver,
+ sizeof (audiodrv_t),
+ ADEV_NOINPUT | ADEV_SPECIAL |
+ ADEV_DISABLE_VIRTUAL |
+ ADEV_NOSRC, AFMT_S16_NE, devc,
+ -1)) < 0)
+ {
+ devc->audio_dev = -1;
+ return;
+ }
+
+ adev = audio_engines[devc->audio_dev];
+
+ adev->devc = devc;
+ adev->min_channels = 2;
+ adev->max_channels = devc->maxchannels;
+ adev->min_rate = pdev->min_rate;
+ adev->max_rate = pdev->max_rate;
+ adev->rate_source = frontdev;
+ adev->caps |= DSP_CH_MULTI;
+ if (pdev->caps & PCM_CAP_FREERATE)
+ adev->caps |= PCM_CAP_FREERATE;
+
+ devc->open_mode = 0;
+}
+
+#endif