summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_userdev
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_userdev')
-rw-r--r--kernel/drv/oss_userdev/.config2
-rw-r--r--kernel/drv/oss_userdev/.devices1
-rw-r--r--kernel/drv/oss_userdev/.name1
-rw-r--r--kernel/drv/oss_userdev/.params11
-rw-r--r--kernel/drv/oss_userdev/oss_userdev.c352
-rw-r--r--kernel/drv/oss_userdev/oss_userdev.man37
-rw-r--r--kernel/drv/oss_userdev/oss_userdev_devicepair.c1255
-rw-r--r--kernel/drv/oss_userdev/userdev.h81
8 files changed, 1740 insertions, 0 deletions
diff --git a/kernel/drv/oss_userdev/.config b/kernel/drv/oss_userdev/.config
new file mode 100644
index 0000000..828bd4f
--- /dev/null
+++ b/kernel/drv/oss_userdev/.config
@@ -0,0 +1,2 @@
+bus=VIRTUAL
+targetcpu=any
diff --git a/kernel/drv/oss_userdev/.devices b/kernel/drv/oss_userdev/.devices
new file mode 100644
index 0000000..41dd8b4
--- /dev/null
+++ b/kernel/drv/oss_userdev/.devices
@@ -0,0 +1 @@
+oss_userdev oss_userdev OSS user space audio driver I/O module
diff --git a/kernel/drv/oss_userdev/.name b/kernel/drv/oss_userdev/.name
new file mode 100644
index 0000000..4d501dc
--- /dev/null
+++ b/kernel/drv/oss_userdev/.name
@@ -0,0 +1 @@
+OSS loopback audio driver
diff --git a/kernel/drv/oss_userdev/.params b/kernel/drv/oss_userdev/.params
new file mode 100644
index 0000000..45f9032
--- /dev/null
+++ b/kernel/drv/oss_userdev/.params
@@ -0,0 +1,11 @@
+int userdev_visible_clientnodes=0;
+/*
+ * By default the oss_userdev driver will not create private device nodes
+ * for the client side devices. Instead all client devices will share
+ * the same device node (/dev/oss/oss_userdev/client).
+ *
+ * If userdev_visible_clientnodes is set to 1 then each oss_userdev instance
+ * will have private device node (/dev/oss/oss_userdev0/pcmN) that can be
+ * opened directly. This mode can be used when the oss_userdev driver is used
+ * for multiple purposes in the same system.
+ */
diff --git a/kernel/drv/oss_userdev/oss_userdev.c b/kernel/drv/oss_userdev/oss_userdev.c
new file mode 100644
index 0000000..c026ff3
--- /dev/null
+++ b/kernel/drv/oss_userdev/oss_userdev.c
@@ -0,0 +1,352 @@
+/*
+ * Purpose: Kernel space support module for user land audio/mixer drivers
+ *
+ */
+/*
+ *
+ * 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_userdev_cfg.h"
+#include <oss_userdev_exports.h>
+#include "userdev.h"
+
+oss_device_t *userdev_osdev = NULL;
+static int client_dev = -1, server_dev = -1; /* Control devces */
+
+char *userdev_client_devnode = "/dev/oss/oss_userdev0/client";
+char *userdev_server_devnode = "/dev/oss/oss_userdev0/server";
+
+/*
+ * Global device lists and the mutex that protects them.
+ */
+oss_mutex_t userdev_global_mutex;
+
+/*
+ * The oss_userdev driver creates new device pairs on-demand. All device
+ * pairs that are not in use will be kept in the userdev_free_device_list
+ * (linked) list. If this list contains any entries then they will be
+ * reused whenever a new device pair is required.
+ */
+userdev_devc_t *userdev_free_device_list = NULL;
+
+/*
+ * Linked list for device pairs that have a server attached. These device
+ * pairs are available for the clients.
+ */
+userdev_devc_t *userdev_active_device_list = NULL;
+
+/*ARGSUSED*/
+static int
+userdev_server_redirect (int dev, int mode, int open_flags)
+{
+/*
+ * Purpose: This entry point is used to create new userdev instances and to redirect clients to them.
+ */
+ int server_engine;
+
+
+ if ((server_engine=usrdev_find_free_device_pair()) >= 0)
+ {
+ userdev_devc_t *devc = audio_engines[server_engine]->devc;
+
+ userdev_reinit_instance(devc);
+ return server_engine;
+ }
+
+ return userdev_create_device_pair();
+}
+
+/*ARGSUSED*/
+static int
+userdev_client_redirect (int dev, int mode, int open_flags)
+{
+/*
+ * Purpose: This entry point is used to create new userdev instances and to redirect clients to them.
+ */
+
+ userdev_devc_t *devc;
+ oss_native_word flags;
+
+ uid_t uid;
+
+ uid = oss_get_procinfo(OSS_GET_PROCINFO_UID);
+
+ MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
+ devc=userdev_active_device_list;
+
+ while (devc != NULL)
+ {
+ int ok=1;
+
+ switch (devc->match_method)
+ {
+ case UD_MATCH_UID:
+ if (devc->match_key != uid) /* Wrong UID */
+ ok=0;
+ break;
+ }
+
+ if (ok)
+ {
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+ return devc->client_portc.audio_dev;
+ }
+
+ devc = devc->next_instance;
+ }
+
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+ return OSS_EIO;
+}
+
+/*
+ * Dummy audio driver entrypoint functions.
+ *
+ * Functionality of the control device is handled by userdev_[client|server]_redirect().
+ * The other entry points are not used for any purpose but the audio core
+ * framework expects to see them.
+ */
+/*ARGSUSED*/
+static int
+userdev_control_set_rate (int dev, int arg)
+{
+ /* Dumy routine - Not actually used */
+ return 48000;
+}
+
+/*ARGSUSED*/
+static short
+userdev_control_set_channels (int dev, short arg)
+{
+ /* Dumy routine - Not actually used */
+ return 2;
+}
+
+/*ARGSUSED*/
+static unsigned int
+userdev_control_set_format (int dev, unsigned int arg)
+{
+ /* Dumy routine - Not actually used */
+ return AFMT_S16_NE;
+}
+
+static void
+userdev_control_reset (int dev)
+{
+ /* Dumy routine - Not actually used */
+}
+
+/*ARGSUSED*/
+static int
+userdev_control_open (int dev, int mode, int open_flags)
+{
+ /* Dumy routine - Not actually used */
+ return OSS_EIO;
+}
+
+/*ARGSUSED*/
+static void
+userdev_control_close (int dev, int mode)
+{
+ /* Dumy routine - Not actually used */
+}
+
+/*ARGSUSED*/
+static int
+userdev_control_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ /* Dumy routine - Not actually used */
+ return OSS_EINVAL;
+}
+
+/*ARGSUSED*/
+static void
+userdev_control_output_block (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+ /* Dumy routine - Not actually used */
+}
+
+/*ARGSUSED*/
+static void
+userdev_control_start_input (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+ /* Dumy routine - Not actually used */
+}
+
+/*ARGSUSED*/
+static int
+userdev_control_prepare_for_input (int dev, int bsize, int bcount)
+{
+ /* Dumy routine - Not actually used */
+ return OSS_EIO;
+}
+
+/*ARGSUSED*/
+static int
+userdev_control_prepare_for_output (int dev, int bsize, int bcount)
+{
+ /* Dumy routine - Not actually used */
+ return OSS_EIO;
+}
+
+static audiodrv_t userdev_server_control_driver = {
+ userdev_control_open,
+ userdev_control_close,
+ userdev_control_output_block,
+ userdev_control_start_input,
+ userdev_control_ioctl,
+ userdev_control_prepare_for_input,
+ userdev_control_prepare_for_output,
+ userdev_control_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* trigger */
+ userdev_control_set_rate,
+ userdev_control_set_format,
+ userdev_control_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ userdev_server_redirect
+};
+
+static audiodrv_t userdev_client_control_driver = {
+ userdev_control_open,
+ userdev_control_close,
+ userdev_control_output_block,
+ userdev_control_start_input,
+ userdev_control_ioctl,
+ userdev_control_prepare_for_input,
+ userdev_control_prepare_for_output,
+ userdev_control_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* trigger */
+ userdev_control_set_rate,
+ userdev_control_set_format,
+ userdev_control_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ userdev_client_redirect
+};
+
+static void
+attach_control_device(void)
+{
+/*
+ * Create the control device files that are used to create client/server
+ * device pairs and to redirect access to them.
+ */
+ userdev_devc_t *devc = (userdev_devc_t*)0xdeadcafe; /* This should never get referenced */
+
+ if ((client_dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ userdev_osdev,
+ userdev_osdev,
+ "User space audio device client side",
+ &userdev_client_control_driver,
+ sizeof (audiodrv_t),
+ ADEV_AUTOMODE, AFMT_S16_NE, devc, -1,
+ "client")) < 0)
+ {
+ return;
+ }
+ userdev_server_devnode = audio_engines[server_dev]->devnode;
+ audio_engines[client_dev]->vmix_mixer=NULL;
+
+ if ((server_dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ userdev_osdev,
+ userdev_osdev,
+ "User space audio device server side",
+ &userdev_server_control_driver,
+ sizeof (audiodrv_t),
+ ADEV_AUTOMODE, AFMT_S16_NE, devc, -1,
+ "server")) < 0)
+ {
+ return;
+ }
+ audio_engines[server_dev]->caps |= PCM_CAP_HIDDEN;
+ audio_engines[server_dev]->vmix_mixer=NULL;
+ userdev_client_devnode = audio_engines[client_dev]->devnode;
+}
+
+int
+oss_userdev_attach (oss_device_t * osdev)
+{
+ userdev_osdev = osdev;
+
+ osdev->devc = NULL;
+ MUTEX_INIT (osdev, userdev_global_mutex, MH_DRV);
+
+ oss_register_device (osdev, "User space audio driver subsystem");
+
+ attach_control_device();
+
+ return 1;
+}
+
+int
+oss_userdev_detach (oss_device_t * osdev)
+{
+ userdev_devc_t *devc;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ devc = userdev_free_device_list;
+
+ while (devc != NULL)
+ {
+ userdev_devc_t *next = devc->next_instance;
+
+ userdev_delete_device_pair(devc);
+
+ devc = next;
+ }
+
+ oss_unregister_device (osdev);
+
+ MUTEX_CLEANUP(userdev_global_mutex);
+
+ return 1;
+}
diff --git a/kernel/drv/oss_userdev/oss_userdev.man b/kernel/drv/oss_userdev/oss_userdev.man
new file mode 100644
index 0000000..cdb0d12
--- /dev/null
+++ b/kernel/drv/oss_userdev/oss_userdev.man
@@ -0,0 +1,37 @@
+NAME
+oss_userdev - OSS client/server audio pseudo device.
+
+NOTICE
+This audio device is not designed to be used as-is by the users. It requires
+a specially designed server application that implements the actual service
+(please see the OSS programming documentation for more info). The server
+application will then create the audio devices that can be used to record
+and/or play audio.
+
+DESCRIPTION
+The oss_userdev driver is a special purpose loop back audio device that can be
+used when implementing OSS audio devices based on a server running in the
+background.
+
+OPTIONS
+
+o userdev_visible_clientnodes=0|1
+By default (0) common client device node (/dev/oss/oss_userdev0/client) will
+be created for all server instances. The clients will then get directed to the
+right instance based on some search criteria (for example UID). This
+alternative is best when using single server application that can serve large
+number of different sesions.
+
+If this option
+is set to 1 then OSS will create separate client device nodes for each
+instance. Applications will have to open the right device nodes. This
+alternative is best when oss_userdev is used to create different kind of
+services in one system. In this way for example a VoIP link can be accessed
+by opening a different device node than when opening some other service.
+
+FILES
+CONFIGFILEPATH/oss_userdev.conf Device configuration file.
+
+AUTHOR
+4Front Technologies
+
diff --git a/kernel/drv/oss_userdev/oss_userdev_devicepair.c b/kernel/drv/oss_userdev/oss_userdev_devicepair.c
new file mode 100644
index 0000000..8119cb9
--- /dev/null
+++ b/kernel/drv/oss_userdev/oss_userdev_devicepair.c
@@ -0,0 +1,1255 @@
+/*
+ * Purpose: Client/server audio device pair for oss_userdev
+ *
+ * This file implements the actual client/server device pair. There will be
+ * separate oss_userdev instance for each process that has opened the
+ * client side.
+ */
+/*
+ *
+ * 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_userdev_cfg.h"
+#include <oss_userdev_exports.h>
+#include "userdev.h"
+static void userdev_free_device_pair (userdev_devc_t *devc);
+
+extern int userdev_visible_clientnodes;
+
+static void
+transfer_audio (userdev_portc_t * server_portc, dmap_t * dmap_from,
+ dmap_t * dmap_to)
+{
+ int l = dmap_from->fragment_size;
+ unsigned char *fromp, *top;
+
+ if (dmap_to->fragment_size != l)
+ {
+ cmn_err (CE_WARN, "Fragment size mismatch (%d != %d)\n",
+ dmap_to->fragment_size, l);
+
+ /* Perform emergency stop */
+ server_portc->input_triggered = 0;
+ server_portc->output_triggered = 0;
+ server_portc->peer->input_triggered = 0;
+ server_portc->peer->output_triggered = 0;
+ return;
+ }
+
+ fromp =
+ dmap_from->dmabuf + (dmap_from->byte_counter % dmap_from->bytes_in_use);
+ top = dmap_to->dmabuf + (dmap_to->byte_counter % dmap_to->bytes_in_use);
+
+ memcpy (top, fromp, l);
+
+}
+
+static void
+handle_input (userdev_portc_t * server_portc)
+{
+ userdev_portc_t *client_portc = server_portc->peer;
+
+ if (client_portc->output_triggered)
+ {
+ transfer_audio (server_portc,
+ audio_engines[client_portc->audio_dev]->dmap_out,
+ audio_engines[server_portc->audio_dev]->dmap_in);
+ oss_audio_outputintr (client_portc->audio_dev, 0);
+ }
+
+ oss_audio_inputintr (server_portc->audio_dev, 0);
+}
+
+static void
+handle_output (userdev_portc_t * server_portc)
+{
+ userdev_portc_t *client_portc = server_portc->peer;
+
+ if (client_portc->input_triggered)
+ {
+ transfer_audio (server_portc,
+ audio_engines[server_portc->audio_dev]->dmap_out,
+ audio_engines[client_portc->audio_dev]->dmap_in);
+ oss_audio_inputintr (client_portc->audio_dev, 0);
+ }
+
+ oss_audio_outputintr (server_portc->audio_dev, 0);
+}
+
+static void
+userdev_cb (void *pc)
+{
+/*
+ * This timer callback routine will get called 100 times/second. It handles
+ * movement of audio data between the client and server sides.
+ */
+ userdev_portc_t *server_portc = pc;
+ userdev_devc_t *devc = server_portc->devc;
+ int tmout = devc->poll_ticks;
+
+ if (tmout < 1)
+ tmout = 1;
+
+ devc->timeout_id = 0; /* No longer valid */
+
+ if (server_portc->input_triggered)
+ handle_input (server_portc);
+
+ if (server_portc->output_triggered)
+ handle_output (server_portc);
+
+ /* Retrigger timer callback */
+ if (server_portc->input_triggered || server_portc->output_triggered)
+ devc->timeout_id = timeout (userdev_cb, server_portc, tmout);
+}
+
+static int
+userdev_check_input (int dev)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ if (!portc->peer->output_triggered)
+ {
+ return OSS_ECONNRESET;
+ }
+ return 0;
+}
+
+static int
+userdev_check_output (int dev)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+
+ if (!portc->peer->input_triggered)
+ {
+ return OSS_ECONNRESET;
+ }
+
+ if (portc->peer->open_mode == 0)
+ return OSS_EIO;
+ return 0;
+}
+
+static void
+setup_sample_format (userdev_portc_t * portc)
+{
+ adev_t *adev;
+ userdev_devc_t *devc = portc->devc;
+ int fragsize, frame_size;
+
+ frame_size = devc->channels * devc->fmt_bytes;
+ if (frame_size == 0)
+ frame_size = 4;
+
+ fragsize = (devc->rate * frame_size * devc->poll_ticks) / OSS_HZ; /* Number of bytes/fragment */
+ devc->rate = fragsize * 100 / frame_size;
+
+/* Setup the server side */
+ adev = audio_engines[portc->audio_dev];
+ adev->min_block = adev->max_block = fragsize;
+
+/* Setup the client side */
+ adev = audio_engines[portc->peer->audio_dev];
+ adev->min_block = adev->max_block = fragsize;
+
+ adev->max_rate = adev->min_rate = devc->rate;
+ adev->iformat_mask = devc->fmt;
+ adev->oformat_mask = devc->fmt;
+ adev->xformat_mask = devc->fmt;
+ adev->min_channels = adev->max_channels = devc->channels;
+}
+
+static int
+userdev_server_set_rate (int dev, int arg)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ adev_t *client_adev = audio_engines[portc->peer->audio_dev];
+
+ if (arg == 0)
+ return devc->rate;
+
+ if (portc->peer->input_triggered || portc->peer->output_triggered)
+ return devc->rate;
+
+ if (arg < 5000)
+ arg = 5000;
+ if (arg > MAX_RATE)
+ arg = MAX_RATE;
+
+ /* Force the sample rate to be multiple of 100 */
+ arg = (arg / 100) * 100;
+
+ devc->rate = arg;
+
+ client_adev->min_rate = arg;
+ client_adev->max_rate = arg;
+
+ setup_sample_format (portc);
+
+ return devc->rate = arg;
+}
+
+/*ARGSUSED*/
+static int
+userdev_client_set_rate (int dev, int arg)
+{
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ return devc->rate;
+}
+
+static short
+userdev_server_set_channels (int dev, short arg)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ adev_t *client_adev = audio_engines[portc->peer->audio_dev];
+
+ if (arg == 0)
+ return devc->channels;
+
+ if (portc->peer->input_triggered || portc->peer->output_triggered)
+ return devc->channels;
+
+ if (arg < 1)
+ arg = 1;
+ if (arg > MAX_CHANNELS)
+ arg = MAX_CHANNELS;
+
+ devc->channels = arg;
+ client_adev->min_channels=client_adev->max_channels=arg;
+
+ setup_sample_format (portc);
+
+ return devc->channels;
+}
+
+/*ARGSUSED*/
+static short
+userdev_client_set_channels (int dev, short arg)
+{
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ return devc->channels; /* Server side channels */
+}
+
+static unsigned int
+userdev_server_set_format (int dev, unsigned int arg)
+{
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ adev_t *client_adev = audio_engines[portc->peer->audio_dev];
+
+ if (arg == 0)
+ return devc->fmt;
+
+ if (portc->peer->input_triggered || portc->peer->output_triggered)
+ return devc->fmt;
+
+ switch (arg)
+ {
+ case AFMT_S16_NE:
+ devc->fmt_bytes = 2;
+ break;
+
+ case AFMT_S32_NE:
+ devc->fmt_bytes = 4;
+ break;
+
+ default: /* Unsupported format */
+ arg = AFMT_S16_NE;
+ devc->fmt_bytes = 2;
+
+ }
+
+ devc->fmt = arg;
+
+ client_adev->oformat_mask = arg;
+ client_adev->iformat_mask = arg;
+
+ setup_sample_format (portc);
+
+ return devc->fmt;
+}
+
+/*ARGSUSED*/
+static unsigned int
+userdev_client_set_format (int dev, unsigned int arg)
+{
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ return devc->fmt; /* Server side sample format */
+}
+
+static void userdev_trigger (int dev, int state);
+
+static void
+userdev_reset (int dev)
+{
+ userdev_trigger (dev, 0);
+}
+
+/*ARGSUSED*/
+static int
+userdev_server_open (int dev, int mode, int open_flags)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ if (portc == NULL || portc->peer == NULL)
+ return OSS_ENXIO;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ devc->open_count++;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_client_open (int dev, int mode, int open_flags)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ if (portc == NULL || portc->peer == NULL)
+ return OSS_ENXIO;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (portc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ devc->open_count++;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+}
+
+static void
+wipe_audio_buffers(userdev_devc_t *devc)
+{
+/*
+ * Write silence to the audio buffers when only one of the sides
+ * is open. This prevents the device from looping the last fragments
+ * written to the device.
+ */
+ dmap_t *dmap;
+
+ dmap = audio_engines[devc->client_portc.audio_dev]->dmap_out;
+ if (dmap != NULL && dmap->dmabuf != NULL)
+ memset(dmap->dmabuf, 0, dmap->buffsize);
+
+ dmap = audio_engines[devc->client_portc.audio_dev]->dmap_in;
+ if (dmap != NULL && dmap->dmabuf != NULL)
+ memset(dmap->dmabuf, 0, dmap->buffsize);
+
+ dmap = audio_engines[devc->server_portc.audio_dev]->dmap_out;
+ if (dmap != NULL && dmap->dmabuf != NULL)
+ memset(dmap->dmabuf, 0, dmap->buffsize);
+
+ dmap = audio_engines[devc->server_portc.audio_dev]->dmap_in;
+ if (dmap != NULL && dmap->dmabuf != NULL)
+ memset(dmap->dmabuf, 0, dmap->buffsize);
+}
+
+/*ARGSUSED*/
+static void
+userdev_server_close (int dev, int mode)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int open_count;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->open_mode = 0;
+
+ /* Stop the client side because there is no server */
+ portc->peer->input_triggered = 0;
+ portc->peer->output_triggered = 0;
+ open_count = --devc->open_count;
+
+ if (open_count == 0)
+ userdev_free_device_pair (devc);
+ else
+ wipe_audio_buffers(devc);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static void
+userdev_client_close (int dev, int mode)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int open_count;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->open_mode = 0;
+
+ open_count = --devc->open_count;
+
+ if (open_count == 0)
+ userdev_free_device_pair (devc);
+ else
+ wipe_audio_buffers(devc);
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+userdev_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_GETLABEL:
+ {
+ /*
+ * Return an empty string so that this feature can be tested.
+ * Complete functionality is to be implemented later.
+ */
+ oss_label_t *s = (oss_label_t *) arg;
+ memset (s, 0, sizeof (oss_label_t));
+ return 0;
+ }
+ break;
+
+ case SNDCTL_GETSONG:
+ {
+ /*
+ * Return an empty string so that this feature can be tested.
+ * Complete functionality is to be implemented later.
+ */
+ oss_longname_t *s = (oss_longname_t *) arg;
+ memset (s, 0, sizeof (oss_longname_t));
+ return 0;
+ }
+ break;
+ }
+
+ return OSS_EINVAL;
+}
+
+static void
+set_adev_name(int dev, const char *name)
+{
+ adev_t *adev = audio_engines[dev];
+
+ strcpy(adev->name, name);
+
+#ifdef CONFIG_OSS_VMIX
+ if (adev->vmix_mixer != NULL)
+ vmix_change_devnames(adev->vmix_mixer, name);
+#endif
+
+}
+
+static int
+create_instance(int dev, userdev_create_t *crea)
+{
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ char tmp_name[64];
+
+ devc->match_method = crea->match_method;
+ devc->match_key = crea->match_key;
+ devc->create_flags = crea->flags;
+
+ devc->poll_ticks = (crea->poll_interval * OSS_HZ) / 1000;
+
+ if (devc->poll_ticks < 1)
+ devc->poll_ticks = 1;
+
+ crea->poll_interval = (1000*devc->poll_ticks) / OSS_HZ;
+
+ if (crea->poll_interval<1)
+ crea->poll_interval = 1;
+
+ crea->name[sizeof(crea->name)-1]=0; /* Overflow protectgion */
+
+ sprintf(tmp_name, "%s (server)", crea->name);
+ tmp_name[49]=0;
+ set_adev_name (devc->client_portc.audio_dev, crea->name);
+ set_adev_name (devc->server_portc.audio_dev, tmp_name);
+
+ strcpy(crea->devnode, audio_engines[devc->client_portc.audio_dev]->devnode);
+
+ return 0;
+}
+
+static int
+userdev_set_control (int dev, int ctl, unsigned int cmd, int value)
+{
+ userdev_devc_t *devc = mixer_devs[dev]->devc;
+
+ if (ctl < 0 || ctl >= USERDEV_MAX_MIXERS)
+ return OSS_EINVAL;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ return devc->mixer_values[ctl];
+ }
+
+ devc->mixer_values[ctl] = value;
+ devc->modify_counter++;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_server_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ switch (cmd)
+ {
+ case USERDEV_CREATE_INSTANCE:
+ {
+ userdev_create_t *crea = (userdev_create_t *)arg;
+
+ return create_instance(dev, crea);
+ }
+ break;
+
+ case USERDEV_GET_CLIENTCOUNT:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ return *arg = (devc->client_portc.open_mode != 0);
+ }
+ break;
+
+ /*
+ * Mixer related ioctl calls
+ */
+
+ case USERDEV_CREATE_MIXGROUP:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ userdev_mixgroup_t *grp=(userdev_mixgroup_t*)arg;
+ int group;
+
+ grp->name[sizeof(grp->name)-1]=0; /* Buffer overflow protection */
+ if ((group=mixer_ext_create_group(devc->mixer_dev, grp->parent, grp->name))<0)
+ return group;
+ grp->num = group;
+ return 0;
+ }
+ break;
+
+ case USERDEV_CREATE_MIXCTL:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ userdev_mixctl_t *c=(userdev_mixctl_t*)arg;
+ oss_mixext *ext;
+ int ctl;
+
+ c->name[sizeof(c->name)-1]=0; /* Buffer overflow protection */
+
+ if (c->index < 0 || c->index >= USERDEV_MAX_MIXERS)
+ return OSS_EINVAL;
+
+ if ((ctl = mixer_ext_create_control (devc->mixer_dev,
+ c->parent,
+ c->index,
+ userdev_set_control,
+ c->type,
+ c->name,
+ c->maxvalue,
+ c->flags)) < 0)
+ return ctl;
+
+ c->num = ctl;
+ ext = mixer_find_ext (devc->mixer_dev, ctl);
+
+ ext->minvalue = c->offset;
+ ext->control_no= c->control_no;
+ ext->rgbcolor = c->rgbcolor;
+
+ if (c->type == MIXT_ENUM)
+ {
+ memcpy(ext->enum_present, c->enum_present, sizeof(ext->enum_present));
+ mixer_ext_set_strings (devc->mixer_dev, ctl, c->enum_choises, 0);
+ }
+
+ return 0;
+ }
+ break;
+
+ case USERDEV_GET_MIX_CHANGECOUNT:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ return *arg = devc->modify_counter;
+ }
+ break;
+
+ case USERDEV_SET_MIXERS:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ memcpy(devc->mixer_values, arg, sizeof(devc->mixer_values));
+ mixer_devs[devc->mixer_dev]->modify_counter++;
+//cmn_err(CE_CONT, "Set %08x %08x\n", devc->mixer_values[0], devc->mixer_values[1]);
+ return 0;
+ }
+ break;
+
+ case USERDEV_GET_MIXERS:
+ {
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ memcpy(arg, devc->mixer_values, sizeof(devc->mixer_values));
+ return 0;
+ }
+ break;
+
+ }
+
+ return userdev_ioctl(dev, cmd, arg);
+}
+
+/*ARGSUSED*/
+static void
+userdev_output_block (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+/*ARGSUSED*/
+static void
+userdev_start_input (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+static void
+userdev_trigger (int dev, int state)
+{
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ if (portc->open_mode & OPEN_READ) /* Handle input */
+ {
+ portc->input_triggered = !!(state & OPEN_READ);
+ }
+
+ if (portc->open_mode & OPEN_WRITE) /* Handle output */
+ {
+ portc->output_triggered = !!(state & OPEN_WRITE);
+ }
+
+ if (portc->output_triggered || portc->input_triggered) /* Something is going on */
+ {
+ int tmout = devc->poll_ticks;
+
+ if (tmout < 1)
+ tmout = 1;
+
+ if (portc->port_type != PT_SERVER)
+ portc = portc->peer; /* Switch to the server side */
+
+ if (portc->output_triggered || portc->input_triggered) /* Something is going on */
+ if (devc->timeout_id == 0)
+ {
+ devc->timeout_id = timeout (userdev_cb, portc, tmout);
+ }
+ }
+ else
+ {
+ if (portc->port_type == PT_SERVER)
+ if (devc->timeout_id != 0)
+ {
+ untimeout (devc->timeout_id);
+ devc->timeout_id = 0;
+ }
+ }
+}
+
+/*ARGSUSED*/
+static int
+userdev_server_prepare_for_input (int dev, int bsize, int bcount)
+{
+ oss_native_word flags;
+
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->input_triggered = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_server_prepare_for_output (int dev, int bsize, int bcount)
+{
+ oss_native_word flags;
+
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->output_triggered = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_client_prepare_for_input (int dev, int bsize, int bcount)
+{
+ oss_native_word flags;
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->input_triggered = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_client_prepare_for_output (int dev, int bsize, int bcount)
+{
+ oss_native_word flags;
+ userdev_portc_t *portc = audio_engines[dev]->portc;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->output_triggered = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+userdev_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
+userdev_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
+userdev_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+}
+#endif
+
+/*ARGSUSED*/
+static int
+userdev_ioctl_override (int dev, unsigned int cmd, ioctl_arg arg)
+{
+/*
+ * Purpose of this ioctl override function is to intercept mixer
+ * ioctl calls made on the client side and to hide everything
+ * outside the userdev instance from the application.
+ *
+ * Note that this ioctl is related with the client side audio device. However
+ * if /dev/mixer points to this (audio) device then all mixer acess will
+ * be redirected too. Also the vmix driver will redirect mixer/system ioctl
+ * calls to this function.
+ */
+ int err;
+ userdev_devc_t *devc = audio_engines[dev]->devc;
+ adev_t *adev = audio_engines[devc->client_portc.audio_dev];
+
+ switch (cmd)
+ {
+ case SNDCTL_MIX_NRMIX:
+ return *arg=1;
+ break;
+
+ case SNDCTL_MIX_NREXT:
+ *arg = devc->mixer_dev;
+ return OSS_EAGAIN; /* Continue with the default handler */
+ break;
+
+ case SNDCTL_SYSINFO:
+ {
+ /*
+ * Fake SNDCTL_SYSINFO to report just one mixer device which is
+ * the one associated with the client.
+ */
+ oss_sysinfo *info = (oss_sysinfo *) arg;
+ int i;
+
+ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
+ return err;
+
+ /*
+ * Hide all non-oss_userdev devices
+ */
+ strcpy (info->product, "OSS (userdev)");
+ info->nummixers = 1;
+ info->numcards = 1;
+ info->numaudios = 1;
+ for (i = 0; i < 8; i++)
+ info->openedaudio[i] = 0;
+
+ return 0;
+ }
+ break;
+
+ case SNDCTL_MIXERINFO:
+ {
+ oss_mixerinfo *info = (oss_mixerinfo *) arg;
+
+ info->dev = devc->mixer_dev; /* Redirect to oss_userdev mixer */
+
+ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
+ return err;
+
+ strcpy(info->name, adev->name);
+ info->card_number = 0;
+ return 0;
+ }
+
+ case SNDCTL_AUDIOINFO:
+ case SNDCTL_AUDIOINFO_EX:
+ {
+ oss_audioinfo *info = (oss_audioinfo *) arg;
+
+ info->dev = devc->client_portc.audio_dev;
+
+ cmd = SNDCTL_ENGINEINFO;
+
+ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
+ return err;
+
+ info->card_number = 0;
+ return 0;
+ }
+
+ case SNDCTL_CARDINFO:
+ {
+ oss_card_info *info = (oss_card_info *) arg;
+
+ info->card = adev->card_number; /* Redirect to oss_userdev0 */
+
+ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
+ return err;
+
+ info->card = 0;
+ return 0;
+ }
+
+ case SNDCTL_MIX_EXTINFO:
+ {
+ oss_mixext *ext = (oss_mixext*)arg;
+
+ ext->dev = devc->mixer_dev;
+
+ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
+ return err;
+
+ if (ext->type == MIXT_DEVROOT)
+ {
+ oss_mixext_root *root = (oss_mixext_root *) ext->data;
+ strncpy(root->name, adev->name, 48);
+ root->name[47]=0;
+ }
+
+ return 0;
+ }
+ break;
+
+ case SNDCTL_MIX_READ:
+ case SNDCTL_MIX_WRITE:
+ {
+ oss_mixer_value *ent = (oss_mixer_value*)arg;
+
+ ent->dev = devc->mixer_dev;
+
+ return OSS_EAGAIN; /* Redirect */
+ }
+ break;
+
+ default:
+ return OSS_EAGAIN;
+ }
+}
+
+static audiodrv_t userdev_server_driver = {
+ userdev_server_open,
+ userdev_server_close,
+ userdev_output_block,
+ userdev_start_input,
+ userdev_server_ioctl,
+ userdev_server_prepare_for_input,
+ userdev_server_prepare_for_output,
+ userdev_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ userdev_trigger,
+ userdev_server_set_rate,
+ userdev_server_set_format,
+ userdev_server_set_channels,
+ NULL,
+ NULL,
+ userdev_check_input,
+ userdev_check_output,
+ userdev_alloc_buffer,
+ userdev_free_buffer,
+ NULL,
+ NULL,
+ NULL /* userdev_get_buffer_pointer */
+};
+
+static audiodrv_t userdev_client_driver = {
+ userdev_client_open,
+ userdev_client_close,
+ userdev_output_block,
+ userdev_start_input,
+ userdev_ioctl,
+ userdev_client_prepare_for_input,
+ userdev_client_prepare_for_output,
+ userdev_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ userdev_trigger,
+ userdev_client_set_rate,
+ userdev_client_set_format,
+ userdev_client_set_channels,
+ NULL,
+ NULL,
+ userdev_check_input,
+ userdev_check_output,
+ userdev_alloc_buffer,
+ userdev_free_buffer,
+ NULL,
+ NULL,
+ NULL, // userdev_get_buffer_pointer
+ NULL, // userdev_calibrate_speed
+ NULL, // userdev_sync_control
+ NULL, // userdev_prepare_to_stop
+ NULL, // userdev_get_input_pointer
+ NULL, // userdev_get_output_pointer
+ NULL, // userdev_bind
+ NULL, // userdev_setup_fragments
+ NULL, // userdev_redirect
+ userdev_ioctl_override
+};
+
+static int
+userdev_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+
+ if (cmd == SOUND_MIXER_READ_CAPS)
+ return *arg = SOUND_CAP_NOLEGACY;
+
+#if 0
+ if (cmd == SOUND_MIXER_READ_DEVMASK ||
+ cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC)
+ return *arg = 0;
+
+ if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM)
+ return *arg = 100 | (100 << 8);
+ if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM)
+ return *arg = 100 | (100 << 8);
+#endif
+ return OSS_EINVAL;
+}
+
+static mixer_driver_t userdev_mixer_driver = {
+ userdev_mixer_ioctl
+};
+
+static int
+install_server (userdev_devc_t * devc)
+{
+ userdev_portc_t *portc = &devc->server_portc;
+ int adev;
+
+ int opts =
+ ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL |
+ ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_HIDDEN | ADEV_DUPLEX;
+
+ memset (portc, 0, sizeof (*portc));
+
+ portc->devc = devc;
+ portc->port_type = PT_SERVER;
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "User space audio device server side",
+ &userdev_server_driver,
+ sizeof (audiodrv_t),
+ opts, SUPPORTED_FORMATS, devc, -1)) < 0)
+ {
+ return adev;
+ }
+
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = MAX_RATE;
+ audio_engines[adev]->min_channels = 1;
+ audio_engines[adev]->max_channels = MAX_CHANNELS;
+ audio_engines[adev]->vmix_mixer=NULL;
+ strcpy(audio_engines[adev]->devnode, userdev_server_devnode);
+
+ portc->audio_dev = adev;
+
+ return adev;
+}
+
+static int
+null_mixer_init(int de)
+{
+ return 0;
+}
+
+static void
+userdev_create_mixer(userdev_devc_t * devc)
+{
+ if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "OSS userdev mixer",
+ &userdev_mixer_driver,
+ sizeof (mixer_driver_t), devc)) < 0)
+ {
+ devc->mixer_dev = -1;
+ return;
+ }
+ mixer_ext_set_init_fn (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2);
+}
+
+static int
+install_client (userdev_devc_t * devc)
+{
+ userdev_portc_t *portc = &devc->client_portc;
+ int adev;
+
+ int opts =
+ ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL | ADEV_DUPLEX |
+ ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_LOOP;
+
+ memset (portc, 0, sizeof (*portc));
+
+ userdev_create_mixer(devc);
+
+ portc->devc = devc;
+ portc->port_type = PT_CLIENT;
+
+ if (!userdev_visible_clientnodes && !(devc->create_flags & USERDEV_F_VMIX_PRIVATENODE))
+ {
+ opts |= ADEV_HIDDEN;
+ }
+
+ if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "User space audio device",
+ &userdev_client_driver,
+ sizeof (audiodrv_t),
+ opts, SUPPORTED_FORMATS, devc, -1)) < 0)
+ {
+ return adev;
+ }
+
+ if (!userdev_visible_clientnodes) /* Invisible client device nodes */
+ strcpy(audio_engines[adev]->devnode, userdev_client_devnode);
+
+ audio_engines[adev]->portc = portc;
+ audio_engines[adev]->mixer_dev = devc->mixer_dev;
+ audio_engines[adev]->min_rate = 5000;
+ audio_engines[adev]->max_rate = MAX_RATE;
+ audio_engines[adev]->min_channels = 1;
+ audio_engines[adev]->max_channels = MAX_CHANNELS;
+
+ portc->audio_dev = adev;
+#ifdef CONFIG_OSS_VMIX
+ vmix_attach_audiodev(devc->osdev, adev, -1, 0);
+#endif
+
+ return adev;
+}
+
+int
+userdev_create_device_pair(void)
+{
+ int client_engine, server_engine;
+ userdev_devc_t *devc;
+ oss_native_word flags;
+
+ if ((devc=PMALLOC(userdev_osdev, sizeof (*devc))) == NULL)
+ return OSS_ENOMEM;
+ memset(devc, 0, sizeof(*devc));
+
+ devc->osdev = userdev_osdev;
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+ devc->active=1;
+
+ devc->rate = 48000;
+ devc->fmt = AFMT_S16_NE;
+ devc->fmt_bytes = 2;
+ devc->channels = 2;
+ devc->poll_ticks = 10;
+
+ if ((server_engine=install_server (devc)) < 0)
+ return server_engine;
+
+ if ((client_engine=install_client (devc)) < 0)
+ return client_engine;
+
+ devc->client_portc.peer = &devc->server_portc;
+ devc->server_portc.peer = &devc->client_portc;
+
+ /*
+ * Insert the device to the list of available devices
+ */
+ MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
+ devc->next_instance = userdev_active_device_list;
+ userdev_active_device_list = devc;
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+
+ return server_engine;
+}
+
+static void
+userdev_free_device_pair (userdev_devc_t *devc)
+{
+ oss_native_word flags;
+
+ set_adev_name(devc->client_portc.audio_dev, "User space audio device");
+ set_adev_name(devc->server_portc.audio_dev, "User space audio device server side");
+
+ MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
+
+ devc->match_method = 0;
+ devc->match_key = 0;
+
+ /*
+ * Add to the free device pair list.
+ */
+ devc->next_instance = userdev_free_device_list;
+ userdev_free_device_list = devc;
+
+ /*
+ * Remove the device pair from the active device list.
+ */
+
+ if (userdev_active_device_list == devc) /* First device in the list */
+ {
+ userdev_active_device_list = userdev_active_device_list->next_instance;
+ }
+ else
+ {
+ userdev_devc_t *this = userdev_active_device_list, *prev = NULL;
+
+ while (this != NULL)
+ {
+ if (this == devc)
+ {
+ prev->next_instance = this->next_instance; /* Remove */
+ break;
+ }
+
+ prev = this;
+ this = this->next_instance;
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+}
+
+void
+userdev_reinit_instance(userdev_devc_t *devc)
+{
+ if (devc->mixer_dev < 0)
+ return;
+
+ mixer_ext_rebuild_all (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2);
+}
+
+void
+userdev_delete_device_pair(userdev_devc_t *devc)
+{
+ if (!devc->active)
+ return;
+
+ devc->active = 0;
+ MUTEX_CLEANUP(devc->mutex);
+}
+
+int
+usrdev_find_free_device_pair(void)
+{
+ oss_native_word flags;
+ userdev_devc_t *devc;
+
+ MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
+
+ if (userdev_free_device_list != NULL)
+ {
+ devc = userdev_free_device_list;
+ userdev_free_device_list = userdev_free_device_list->next_instance;
+
+ devc->next_instance = userdev_active_device_list;
+ userdev_active_device_list = devc;
+
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+ return devc->server_portc.audio_dev;
+ }
+ MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
+
+ return OSS_ENXIO;
+}
diff --git a/kernel/drv/oss_userdev/userdev.h b/kernel/drv/oss_userdev/userdev.h
new file mode 100644
index 0000000..1ed1040
--- /dev/null
+++ b/kernel/drv/oss_userdev/userdev.h
@@ -0,0 +1,81 @@
+/*
+ * Purpose: Definition file for the oss_userdev driver
+ *
+ */
+/*
+ *
+ * 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_RATE 192000
+#define MAX_CHANNELS 64
+#define SUPPORTED_FORMATS (AFMT_S16_NE|AFMT_S32_NE)
+
+typedef struct _userdev_devc_t userdev_devc_t;
+typedef struct _userdev_portc_t userdev_portc_t;
+
+struct _userdev_portc_t
+{
+ userdev_devc_t *devc;
+ userdev_portc_t *peer;
+ int audio_dev;
+ int open_mode;
+ int port_type;
+#define PT_CLIENT 1
+#define PT_SERVER 2
+
+ /* State variables */
+ int input_triggered, output_triggered;
+};
+
+struct _userdev_devc_t
+{
+ oss_device_t *osdev;
+ int active;
+ oss_mutex_t mutex;
+
+ int open_count; /* 0=not in use, 2=both client and server in use */
+
+ int create_flags; /* Flags from ioctl(USERDEV_CREATE_INSTANCE) */
+
+ unsigned int poll_ticks; /* Number of clock tickes (OSS_HZ) between polls. */
+
+ unsigned int match_method;
+ unsigned int match_key;
+ int mixer_dev;
+
+ userdev_devc_t *next_instance;
+
+ int rate;
+ int channels;
+ unsigned int fmt, fmt_bytes;
+ timeout_id_t timeout_id;
+
+ userdev_portc_t client_portc;
+ userdev_portc_t server_portc;
+
+ /*
+ * Mixer related fields
+ */
+ int modify_counter;
+ int mixer_values[USERDEV_MAX_MIXERS];
+};
+
+extern oss_device_t *userdev_osdev;
+extern oss_mutex_t userdev_global_mutex;
+extern userdev_devc_t *userdev_active_device_list;
+extern userdev_devc_t *userdev_free_device_list;
+
+extern int userdev_create_device_pair(void);
+extern void userdev_delete_device_pair(userdev_devc_t *devc);
+extern int usrdev_find_free_device_pair(void);
+extern void userdev_reinit_instance(userdev_devc_t *devc);
+
+extern char *userdev_client_devnode;
+extern char *userdev_server_devnode;