diff options
Diffstat (limited to 'kernel/drv/oss_imux')
-rw-r--r-- | kernel/drv/oss_imux/.config | 2 | ||||
-rw-r--r-- | kernel/drv/oss_imux/.devices | 1 | ||||
-rw-r--r-- | kernel/drv/oss_imux/.name | 1 | ||||
-rw-r--r-- | kernel/drv/oss_imux/.params | 24 | ||||
-rw-r--r-- | kernel/drv/oss_imux/oss_imux.c | 1072 | ||||
-rw-r--r-- | kernel/drv/oss_imux/oss_imux.man | 78 |
6 files changed, 1178 insertions, 0 deletions
diff --git a/kernel/drv/oss_imux/.config b/kernel/drv/oss_imux/.config new file mode 100644 index 0000000..b7929da --- /dev/null +++ b/kernel/drv/oss_imux/.config @@ -0,0 +1,2 @@ +bus=VIRTUAL +cflags=-O diff --git a/kernel/drv/oss_imux/.devices b/kernel/drv/oss_imux/.devices new file mode 100644 index 0000000..be685da --- /dev/null +++ b/kernel/drv/oss_imux/.devices @@ -0,0 +1 @@ +oss_imux oss_imux OSS Input Multiplexer diff --git a/kernel/drv/oss_imux/.name b/kernel/drv/oss_imux/.name new file mode 100644 index 0000000..88f4996 --- /dev/null +++ b/kernel/drv/oss_imux/.name @@ -0,0 +1 @@ +OSS Input Multiplexer (IMUX) diff --git a/kernel/drv/oss_imux/.params b/kernel/drv/oss_imux/.params new file mode 100644 index 0000000..c52896c --- /dev/null +++ b/kernel/drv/oss_imux/.params @@ -0,0 +1,24 @@ +/* + * IMUX supports multiple instances, please consult the man page + */ + +int imux_devices=5; +/* + * Number of IMUX client side devices to configure. + * Values: 2-48 Default: 5. + */ + +int imux_masterdev=-1; +/* + * Master physical device to use (ie attach IMUX to a specific soundcard) + * -1 means automatically detect the device. Use the device index numbers + * reported by ossinfo -a. + * Values: 0-N Default: -1 (autodetec) + */ + +int imux_rate=48000; +/* + * Select the base sampling rate for the IMUX device. The base rate must be + * one supported by the actual physical device (as reported by audioinfo -a -v2). + * Values: 8000-192000 Default: 48000 + */ 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; +} diff --git a/kernel/drv/oss_imux/oss_imux.man b/kernel/drv/oss_imux/oss_imux.man new file mode 100644 index 0000000..4d64206 --- /dev/null +++ b/kernel/drv/oss_imux/oss_imux.man @@ -0,0 +1,78 @@ +NAME +oss_imux - Input Muplexer audio driver. + +DESCRIPTION +IMUX is a virtual recording engine that permits up to 8 audio recording +applications to run at the same time. Generally most soundcards permit only a +single recording application to record the input source (via mic, line or CD). +With IMUX, you can use up to 8 recording applications to record the same +source in different sample rates, bits/sample and channels (mono/stereo). +IMUX does all the rate and format conversion in software. The applications +think that they are actually getting data from the physical device. For +instance, if you want to record the input from a CDROM in .wav format at +48Khz/8/Mono and at the same time you want to record the same stream in .mp3 +format in 44.1Khz 16 bit stereo, with IMUX, you can start the wave recording +application on the first IMUX device and start the MP3 encoder on the second +IMUX device and both applications will run simultaneously thinking that they +are getting data from the physical soundcard. + +CONFIGURATION +To add the IMUX driver you first need to ensure that there is a physical +soundcard present and then you can run ossdetect -i to add it. You may want +to select a master device by setting imux_masterdev, but the autodetection +should provide a good default. After OSS restart, imux should be available. + +USAGE +Connect an input source to the soundcard's line-in jack. Using the Mixer app +like ossmix (or any OSS compliant mixer) set the recording source to Line-In +(eg ossmix line.rec ON) Now you can start recording the input in multiple +formats and at different sample rates. The simplest example is: + + ossrecord -s48000 -b16 -c2 -d/dev/oss/oss_imux0/pcmin0 test1.wav & + ossrecord -s8000 -b8 -d/dev/oss/oss_imux0/pcmin0 test2.wav & + +After a few minutes of recording you can stop them by placing the command in +forground mode (type fg %1 or fg %2) and press ^c to stop. + +You now have two wav format files. test1.wav is a 48KHz 16bit stereo file and +test2.wav is a 8Khz 8bit Mono file. + +You can now playback the files as follows: +o ossplay -v test1.wav and it should show you that the file is indeed +48Khz 16bit stereo. + +o ossplay -v test2.wav - you should see that this file is indeed a +8Khz 8bit mono file. + +What you have essentially accomplished is recording a single input stream into +two different formats at the same time. + +You can now extend this analogy to record the input in mp3 format and RealAudio +format simultaneously. + +The IMUX control panel can be displayed by typing ossxmix -d<imux mixer number> +(for e.g. in the above example, IMUX mixer is #2 so we type ossxmix -d2 + +There are record level control sliders for each input channel and it will show +activity when a recording program is active on a particular channel. + + +OPTIONS +o imux_masterdev: Selects which physical device to use as the Master device +for the IMUX driver. +Values: -1: automatically selected by OSS, 1-N: Audio device index of the +master device (as reported by ossinfo -a), Default: -1. + +o imux_rate: Specifies what is the base sampling rate used by the imux driver. +Values: 5000-96000 Default: 48000 + +o imux_devices: Specifies number of Input Multiplexer devices to setup. +Values: 2-48 Default: 5 + + +FILES +CONFIGFILEPATH/oss_imux.conf Device configuration file + +AUTHOR +4Front Technologies + |