diff options
Diffstat (limited to 'kernel/framework/remux')
-rw-r--r-- | kernel/framework/remux/.config | 1 | ||||
-rw-r--r-- | kernel/framework/remux/oss_remux.c | 544 |
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 |