summaryrefslogtreecommitdiff
path: root/misc/samples
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /misc/samples
downloadoss4-upstream.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'misc/samples')
-rw-r--r--misc/samples/ddksample/Makefile18
-rw-r--r--misc/samples/ddksample/ddksample.c282
-rw-r--r--misc/samples/ddksample/ddksample.conf1
-rw-r--r--misc/samples/ddksample/ddksample.h49
-rw-r--r--misc/samples/ddksample/ddksample_audio.c524
-rw-r--r--misc/samples/ddksample/ddksample_misc.c179
-rw-r--r--misc/samples/ddksample/ddksample_mixer.c375
7 files changed, 1428 insertions, 0 deletions
diff --git a/misc/samples/ddksample/Makefile b/misc/samples/ddksample/Makefile
new file mode 100644
index 0000000..1a688e7
--- /dev/null
+++ b/misc/samples/ddksample/Makefile
@@ -0,0 +1,18 @@
+OBJECTS=ddksample.o ddksample_audio.o ddksample_mixer.o ddksample_misc.o
+CFLAGS=-D_KERNEL
+
+all: ddksample
+
+ddksample: $(OBJECTS)
+ ld -dy -r -Ndrv/osscore $(OBJECTS) -o ddksample
+# ld -64 -dy -r -Ndrv/osscore $(OBJECTS) -o ddksample # For 64 bit kernels
+
+install: ddksample
+ cp ddksample /kernel/drv/ddksample
+ cp ddksample.conf /kernel/drv/ddksample.conf
+ -rem_drv ddksample
+ add_drv ddksample
+ ossdetect -d
+
+clean:
+ rm -f ddksample *.o x y z core *~
diff --git a/misc/samples/ddksample/ddksample.c b/misc/samples/ddksample/ddksample.c
new file mode 100644
index 0000000..df7fd64
--- /dev/null
+++ b/misc/samples/ddksample/ddksample.c
@@ -0,0 +1,282 @@
+/*
+ * ddksample.c: OSS DDK sample driver - attach/detach
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/*
+ * Solaris DDI includes
+ */
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+/*
+ * OSS specific includes
+ */
+#include <sys/soundcard.h>
+#include <sys/ossddk/ossddk.h>
+
+/***************************************************
+ * Private types and variables
+ */
+
+#define DRIVER_NICK "ddksample"
+#define DRIVER_FULLNAME "OSS DDK sample device"
+#define DRIVER_TYPE DRV_VIRTUAL
+
+#include "ddksample.h"
+
+/**************************************************/
+
+static int ddksample_getinfo (dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int ddksample_attach (dev_info_t *, ddi_attach_cmd_t);
+static int ddksample_detach (dev_info_t *, ddi_detach_cmd_t);
+
+/* Entry points structure */
+static struct cb_ops ddksample_cb_ops = {
+ oss_open,
+ oss_close,
+ nodev, /* not a block driver */
+ nodev, /* no print routine */
+ nodev, /* no dump routine */
+ oss_read,
+ oss_write,
+ oss_ioctl,
+ nodev, /* no devmap routine */
+ oss_mmap, /* mmap routine */
+ nodev, /* no segmap routine */
+ oss_chpoll, /* no chpoll routine */
+ ddi_prop_op,
+ 0, /* not a STREAMS driver */
+ D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */
+ CB_REV
+};
+
+static struct dev_ops ddksample_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ ddksample_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify - obsolete */
+#if DRIVER_TYPE==DRV_ISA
+ ddksample_probe,
+#else
+ nulldev, /* devo_probe */
+#endif
+ ddksample_attach, /* devo_attach */
+ ddksample_detach, /* devo_detach */
+ nodev, /* devo_reset */
+#if DRIVER_TYPE==DRV_STREAMS
+ &ddksample_streams_cb_ops, /* devi_cb_ops */
+#else
+ &ddksample_cb_ops, /* devi_cb_ops */
+#endif
+ NULL, /* devo_bus_ops */
+ NULL /* devo_power */
+};
+
+static struct modldrv ddksample_modldrv = {
+ &mod_driverops, /* drv_modops */
+ "Sample OSS DDK virtual driver", /* String "OSS" must be included in the name */
+ &ddksample_dev_ops, /* drv_dev_ops */
+};
+
+static struct modlinkage ddksample_modlinkage = {
+ MODREV_1, /* ml_rev */
+ (void *) &ddksample_modldrv, /* ml_linkage */
+ NULL /* NULL terminates the list */
+};
+
+/*
+ * _init, _info, and _fini support loading and unloading the driver.
+ */
+int
+_init (void)
+{
+ int error;
+
+ error = mod_install (&ddksample_modlinkage);
+
+ return error;
+}
+
+int
+_fini (void)
+{
+ int error;
+
+ error = mod_remove (&ddksample_modlinkage);
+ return error;
+}
+
+int
+_info (struct modinfo *modinfop)
+{
+ int error;
+
+ error = mod_info (&ddksample_modlinkage, modinfop);
+
+ return error;
+}
+
+/**************************************************/
+
+
+static int
+ddksample_attach (dev_info_t * dip, ddi_attach_cmd_t cmd)
+{
+ int instance, err;
+ oss_device_t *osdev;
+ ddi_iblock_cookie_t iblock_cookie;
+ ddksample_devc *devc;
+
+ if (cmd != DDI_ATTACH)
+ {
+ cmn_err (CE_WARN, "bad attach cmd %d\n", cmd);
+ return 0;
+ }
+
+ if (dip == NULL)
+ {
+ cmn_err (CE_WARN, "ddksample_attach: dip==NULL\n");
+ return DDI_FAILURE;
+ }
+
+ instance = ddi_get_instance (dip);
+ cmn_err (CE_CONT, "Attach started " DRIVER_NICK "%d\n", instance);
+
+#if 0
+ // Pseudo drivers don't have any iblock_cookie
+
+ if ((err = ddi_get_iblock_cookie (dip, 0, iblock_cookie)) != DDI_SUCCESS)
+ {
+ cmn_err (CE_WARN, "Cannot get iblock cookie (%d)\n", err);
+ return DDI_FAILURE;
+ }
+#endif
+
+ devc = kmem_zalloc (sizeof (*devc), KM_SLEEP);
+
+ mutex_init (&devc->mutex, NULL, MUTEX_DRIVER, NULL /* iblock_cookie */ );
+
+ if ((osdev =
+ ossddk_register_device (OSSDDK_VERSION, dip, DRIVER_TYPE, instance,
+ DRIVER_NICK, iblock_cookie, devc,
+ DRIVER_FULLNAME)) == NULL)
+ {
+ cmn_err (CE_WARN, "Registering OSS DDK driver failed\n");
+ return DDI_FAILURE;
+ }
+
+ devc->osdev = osdev;
+
+ ddksample_mixer_init (devc); /* From ddksample_mixer.c */
+ ddksample_audio_init (devc); /* From ddksample_audio.c */
+
+ ddi_set_driver_private (dip, (caddr_t) osdev);
+ ddi_report_dev (dip);
+
+ return DDI_SUCCESS;
+}
+
+static int
+ddksample_detach (dev_info_t * dip, ddi_detach_cmd_t cmd)
+{
+ oss_device_t *osdev;
+ int err;
+
+ ddksample_devc *devc;
+
+ if (cmd != DDI_DETACH)
+ {
+ cmn_err (CE_WARN, "bad attach cmd %d\n", cmd);
+ return 0;
+ }
+
+ if (dip == NULL)
+ {
+ cmn_err (CE_WARN, "ddksample_detach: dip==NULL\n");
+ return DDI_FAILURE;
+ }
+
+ cmn_err (CE_CONT, "Detach started " DRIVER_NICK "\n");
+ if ((osdev = ddi_get_driver_private (dip)) == NULL)
+ {
+ cmn_err (CE_WARN, "ddi_get_driver_private() failed\n");
+ return DDI_SUCCESS;
+ }
+
+/*
+ * Check if the device can be removed (not busy) and make sure it will not be
+ * opened any more.
+ */
+ if ((err = ossddk_disable_device (osdev)) < 0)
+ return DDI_FAILURE;
+
+ devc = ossddk_osdev_get_devc (osdev);
+
+/*
+ * Stop the device and make sure it will not raise any
+ * interrupts any morfe.
+ */
+
+ ossddk_unregister_device (osdev);
+
+ mutex_destroy (&devc->mutex);
+ kmem_free (devc, sizeof (*devc));
+
+ return DDI_SUCCESS;
+}
+
+/*
+ * Misc routines
+ */
+
+static int
+ddksample_getinfo (dev_info_t * dip, ddi_info_cmd_t cmd, void *arg,
+ void **result)
+{
+ dev_t dev;
+ register int error;
+ int minor_num, instance;
+
+ if (dip == NULL)
+ {
+ cmn_err (CE_WARN, "ddksample_getinfo: dip==NULL\n");
+ return DDI_FAILURE;
+ }
+
+ dev = (dev_t) arg;
+ minor_num = getminor (dev);
+ instance = ddi_get_instance (dip);
+
+ switch (cmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = dip;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *) instance;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ *result = NULL;
+ error = DDI_FAILURE;
+ }
+
+ return (error);
+}
diff --git a/misc/samples/ddksample/ddksample.conf b/misc/samples/ddksample/ddksample.conf
new file mode 100644
index 0000000..c7e9997
--- /dev/null
+++ b/misc/samples/ddksample/ddksample.conf
@@ -0,0 +1 @@
+name="ddksample" parent="pseudo" instance=0;
diff --git a/misc/samples/ddksample/ddksample.h b/misc/samples/ddksample/ddksample.h
new file mode 100644
index 0000000..a0257f5
--- /dev/null
+++ b/misc/samples/ddksample/ddksample.h
@@ -0,0 +1,49 @@
+#ifndef _DDKSAMPLE_H
+#define _DDKSAMPLE_H
+
+// Audio engine (port) control structure.
+
+#define MAX_SUBDEVICE 4
+
+typedef struct
+{
+ int dev;
+ int open_mode; /* OPEN_READ | OPEN_WRITE */
+
+ int speed;
+ int channels;
+ int format, bits;
+
+ int prepare_flags;
+ int audio_active;
+
+ int timeout_value;
+ timeout_id_t timeout_id;
+
+ int left_volume, right_volume;
+ int mute;
+#define DDKSAMPLE_MAX_VOL 100
+
+ int left_peak, right_peak;
+} ddksample_portc;
+
+// Device control structure
+typedef struct
+{
+ oss_device_t *osdev;
+ kmutex_t mutex;
+
+ int mixer_dev;
+ int mainvol_left, mainvol_right;
+
+ int num_portc;
+ ddksample_portc portc[MAX_SUBDEVICE];
+ int audio_open_count; /* Number of channels currently opened */
+
+} ddksample_devc;
+
+extern int ddksample_mixer_init (ddksample_devc * devc);
+extern int ddksample_audio_init (ddksample_devc * devc);
+extern void ddksample_do_math (ddksample_portc * portc, void *buf, int len);
+
+#endif
diff --git a/misc/samples/ddksample/ddksample_audio.c b/misc/samples/ddksample/ddksample_audio.c
new file mode 100644
index 0000000..4330f6b
--- /dev/null
+++ b/misc/samples/ddksample/ddksample_audio.c
@@ -0,0 +1,524 @@
+/*
+ * ddksample_audio.c - OSS DDK sample driver - Audio functionality
+ *
+ * Description:
+ *
+ * This simple audio driver implements an OSS audio device similar to
+ * /dev/zero. The output will be just discarded. Recording will return
+ * silence. In addition the sample parameters (rate, channels and format)
+ * is emulated. Timeout callbacks are used to keep the device running
+ * at the right rate (approximately).
+ *
+ * To demonstrate mixer functionality (together with ddksample_mixer.c) this
+ * driver also computes the peak levels from the output data. Also output
+ * volume controls are implemented.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/*
+ * Solaris DDI includes
+ */
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+/*
+ * OSS specific includes
+ */
+#include <sys/soundcard.h>
+#include <sys/ossddk/ossddk.h>
+
+/***************************************************
+ * Private types and variables
+ */
+
+#include "ddksample.h"
+
+/**************************************************/
+
+/*
+ * This driver emulates periodic interrupts on fragment boundaries using
+ * the timeout(9f) mechanism. Ordinary drivers use device interrupts
+ * for this.
+ */
+
+static void
+ddksample_intr (void *arg)
+{
+ ddksample_portc *portc = arg;
+
+ if (portc->audio_active & PCM_ENABLE_OUTPUT)
+ {
+#if 1
+/*
+ * We will first do some computations on the output data. This is not
+ * necessary in normal drivers. It's here just to make the mixer section
+ * to behave like the mixer of some real device.
+ */
+ int pos;
+ unsigned char *buf;
+ dmap_t *dmap;
+
+ dmap = ossddk_adev_get_dmapout (portc->dev);
+ buf = ossddk_dmap_get_dmabuf (dmap);
+
+ /*
+ * Compute the current playback position (beginning of the
+ * fragment we have just finished).
+ */
+ pos = ossddk_dmap_get_qhead (dmap) * ossddk_dmap_get_fragsize (dmap);
+
+ buf = buf + pos;
+ ddksample_do_math (portc, buf, ossddk_dmap_get_fragsize (dmap));
+#endif
+
+
+
+
+ ossddk_audio_outputintr (portc->dev, 0); /* Acknowledge one fragment */
+ }
+
+ if (portc->audio_active & PCM_ENABLE_INPUT)
+ {
+ ossddk_audio_inputintr (portc->dev); /* Acknowledge one fragment */
+ }
+
+ portc->timeout_id = timeout (ddksample_intr, portc, portc->timeout_value);
+}
+
+static int
+ddksample_set_rate (int dev, int arg)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+
+ if (arg == 0) /* Query - return the current rate */
+ return portc->speed;
+
+/*
+ * If the requested rate is not supported the driver may just return the
+ * current rate. Or preferably it may select the rate that is closest to the
+ * requested one. In this sample driver we use the easy way.
+ */
+
+ if (arg != 16000 && arg != 24000 && arg != 48000)
+ return portc->speed;
+
+/*
+ * The rate was OK so write it down and report it back.
+ */
+
+ return portc->speed = arg;
+}
+
+static short
+ddksample_set_channels (int dev, short arg)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+
+ if (arg == 0) /* Query - return the current setting */
+ return portc->channels;
+
+/*
+ * Assume 2 channels (stereo) if something incorrect is
+ * requested.
+ */
+
+ if (arg != 1 && arg != 2)
+ return 2;
+
+ return portc->channels = arg;
+}
+
+static unsigned int
+ddksample_set_format (int dev, unsigned int arg)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+
+ if (arg == 0) /* Query */
+ return portc->format;
+
+/*
+ * Fall back to 16 bits (native endianess) if some unsupported
+ * rate is requested.
+ */
+
+ if (arg != AFMT_S16_NE && arg != AFMT_S32_NE)
+ return AFMT_S16_NE;
+
+ if (arg == AFMT_S32_NE)
+ portc->bits = 32;
+ else
+ portc->bits = 16;
+
+ return portc->format = arg;
+}
+
+static int
+ddksample_ioctl (int dev, unsigned int cmd, int *arg)
+{
+// TODO
+ return -EINVAL;
+}
+
+static void ddksample_trigger (int dev, int state);
+
+static void
+ddksample_reset (int dev)
+{
+ ddksample_trigger (dev, 0);
+}
+
+static int
+ddksample_open (int dev, int mode, int open_flags)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+ ddksample_devc *devc = ossddk_adev_get_devc (dev);
+
+ mutex_enter (&devc->mutex);
+
+ if (portc->open_mode != 0) /* Device busy */
+ {
+ mutex_exit (&devc->mutex);
+ return -EBUSY;
+ }
+
+ portc->open_mode = mode;
+ devc->audio_open_count++;
+ mutex_exit (&devc->mutex);
+
+ return 0;
+}
+
+static void
+ddksample_close (int dev, int mode)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+ ddksample_devc *devc = ossddk_adev_get_devc (dev);
+
+ ddksample_reset (dev);
+
+ mutex_enter (&devc->mutex);
+ portc->open_mode = 0;
+ devc->audio_open_count--;
+ mutex_exit (&devc->mutex);
+}
+
+static void
+ddksample_trigger (int dev, int state)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if ((state & PCM_ENABLE_OUTPUT)
+ && (portc->prepare_flags & PCM_ENABLE_OUTPUT))
+ {
+ portc->prepare_flags &= ~PCM_ENABLE_OUTPUT;
+
+ /*
+ * Arm the timeout if it was not started yet.
+ */
+ if (portc->audio_active == 0)
+ portc->timeout_id =
+ timeout (ddksample_intr, portc, portc->timeout_value);
+ portc->audio_active |= PCM_ENABLE_OUTPUT;
+ }
+ else
+ if (!(state & PCM_ENABLE_OUTPUT)
+ && (portc->audio_active & PCM_ENABLE_OUTPUT))
+ {
+ portc->audio_active &= ~PCM_ENABLE_OUTPUT;
+ if (portc->audio_active == 0)
+ untimeout (portc->timeout_id);
+ portc->timeout_id = 0;
+ }
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if ((state & PCM_ENABLE_INPUT)
+ && (portc->prepare_flags & PCM_ENABLE_INPUT))
+ {
+ portc->prepare_flags &= ~PCM_ENABLE_INPUT;
+
+ /*
+ * Arm the timeout if it was not started yet.
+ */
+ if (portc->audio_active == 0)
+ portc->timeout_id =
+ timeout (ddksample_intr, portc, portc->timeout_value);
+ portc->audio_active |= PCM_ENABLE_INPUT;
+ }
+ else
+ if (!(state & PCM_ENABLE_INPUT)
+ && (portc->audio_active & PCM_ENABLE_INPUT))
+ {
+ portc->audio_active &= ~PCM_ENABLE_INPUT;
+ if (portc->audio_active == 0)
+ untimeout (portc->timeout_id);
+ portc->timeout_id = 0;
+ }
+ }
+}
+
+static int
+ddksample_prepare_for_input (int dev, int fragsize, int numfrags)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+ int datarate, intrate, tmout;
+
+ cmn_err (CE_CONT, "Input, rate=%d, channels=%d, format=0x%x, bits=%d\n",
+ portc->speed, portc->channels, portc->format, portc->bits);
+
+/*
+ * Compute the number of lbolt ticks between interrupts. This value is needed
+ * for timeout(9f).
+ */
+
+ datarate = portc->speed * portc->channels * portc->bits / 8; /* Bytes per second */
+ intrate = datarate * 10 / fragsize;
+ tmout = hz * 1000 / intrate;
+
+ cmn_err (CE_CONT, "Datarate %d, fragsize %d\n", datarate, fragsize);
+ cmn_err (CE_CONT, "Intrate %d.%d interrupts / sec.\n", intrate / 10,
+ intrate % 10);
+ cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
+
+ tmout = (tmout + 50) / 100; /* Round to the nearest integer value */
+ if (tmout < 1)
+ tmout = 1;
+
+ portc->timeout_value = tmout;
+ portc->prepare_flags |= PCM_ENABLE_INPUT;
+ return 0;
+}
+
+static int
+ddksample_prepare_for_output (int dev, int fragsize, int numfrags)
+{
+ ddksample_portc *portc = ossddk_adev_get_portc (dev);
+ int datarate, intrate, tmout;
+
+ cmn_err (CE_CONT, "Output, rate=%d, channels=%d, format=0x%x, bits=%d\n",
+ portc->speed, portc->channels, portc->format, portc->bits);
+
+/*
+ * Compute the number of lbolt ticks between interrupts. This value is needed
+ * for timeout(9f).
+ */
+
+ datarate = portc->speed * portc->channels * portc->bits / 8; /* Bytes per second */
+ intrate = datarate * 10 / fragsize;
+ tmout = hz * 1000 / intrate;
+
+ cmn_err (CE_CONT, "Datarate %d, fragsize %d\n", datarate, fragsize);
+ cmn_err (CE_CONT, "Intrate %d.%d interrupts / sec.\n", intrate / 10,
+ intrate % 10);
+ cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
+
+ tmout = (tmout + 50) / 100; /* Round to the nearest integer value */
+ if (tmout < 1)
+ tmout = 1;
+
+ portc->timeout_value = tmout;
+ portc->prepare_flags |= PCM_ENABLE_OUTPUT;
+ return 0;
+}
+
+static int
+ddksample_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+#define MY_BUFFSIZE (64*1024)
+ if (ossddk_dmap_get_dmabuf (dmap) != NULL)
+ return 0;
+ ossddk_dmap_set_phys (dmap, 0); /* Not mmap() capable */
+ ossddk_dmap_set_dmabuf (dmap, kmem_zalloc (MY_BUFFSIZE, KM_SLEEP));
+ if (ossddk_dmap_get_dmabuf (dmap) == NULL) /* Alloc failed */
+ return -ENOSPC;
+ ossddk_dmap_set_buffsize (dmap, MY_BUFFSIZE);
+
+ return 0;
+}
+
+static int
+ddksample_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ if (ossddk_dmap_get_dmabuf (dmap) == NULL)
+ return 0;
+ kmem_free (ossddk_dmap_get_dmabuf (dmap), MY_BUFFSIZE);
+
+ ossddk_dmap_set_dmabuf (dmap, NULL);
+ return 0;
+}
+
+#if 0
+static int
+ddksample_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ return 0;
+}
+#endif
+
+static audiodrv_t ddksample_audio_driver = {
+ ddksample_open,
+ ddksample_close,
+ NULL, // output_block
+ NULL, // start_input
+ ddksample_ioctl,
+ ddksample_prepare_for_input,
+ ddksample_prepare_for_output,
+ ddksample_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ddksample_trigger,
+ ddksample_set_rate,
+ ddksample_set_format,
+ ddksample_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ddksample_alloc_buffer,
+ ddksample_free_buffer,
+ NULL,
+ NULL,
+ NULL /* ddksample_get_buffer_pointer */
+};
+
+int
+ddksample_audio_init (ddksample_devc * devc)
+{
+ int dev;
+ int i;
+ static int rates[] = { 16000, 24000, 48000 };
+
+ for (i = 0; i < MAX_SUBDEVICE; i++)
+ {
+ ddksample_portc *portc;
+ int flags;
+
+ flags = ADEV_VIRTUAL | ADEV_DUPLEX;
+
+ if (i > 0)
+ flags |= ADEV_SHADOW;
+
+ if ((dev = ossddk_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "OSS DDK sample device",
+ &ddksample_audio_driver,
+ sizeof (audiodrv_t),
+ flags,
+ AFMT_S16_NE | AFMT_S32_NE,
+ devc, -1)) < 0)
+ return dev;
+
+ portc = &devc->portc[devc->num_portc++];
+
+ portc->dev = dev;
+ portc->speed = 48000;
+ portc->channels = 2;
+ portc->format = AFMT_S16_NE;
+ portc->bits = 16;
+ portc->open_mode = 0;
+ portc->timeout_id = 0;
+ portc->left_volume = DDKSAMPLE_MAX_VOL;
+ portc->right_volume = DDKSAMPLE_MAX_VOL;
+ portc->left_peak = 0;
+ portc->right_peak = 0;
+ portc->mute = 0;
+
+ ossddk_adev_set_portc (dev, portc);
+ ossddk_adev_set_portnum (dev, i);
+ ossddk_adev_set_mixer (dev, devc->mixer_dev);
+
+ ossddk_adev_set_rates (dev, 16000, 48000, 3, rates);
+ ossddk_adev_set_channels (dev, 1, 2); /* Mono and stereo are supported */
+
+ /*
+ * Usually the list of supported audio formats is set when calling
+ * ossddk_install_audiodev(). However in some cases it may be
+ * necessary to change the format list later. Changing it is
+ * also necessary if recording formats are fdifferent than the
+ * playback formats.
+ *
+ * The next lines demonstrates how to do that. Normal drivers should
+ * never do that.
+ */
+ ossddk_adev_set_formats (dev, AFMT_S32_NE | AFMT_S16_NE, // 16 and 32 bit playback
+ AFMT_S16_NE); // Only 16 bit recording
+
+
+ /*
+ * Report stereo as the preferred channel configuration.
+ */
+ ossddk_adev_set_caps (dev, DSP_CH_STEREO);
+
+#if 0
+ /*
+ * Many devices have some limits for the fragment size. For
+ * example it's common that the fragment must fit in a single
+ * memory page. In such cases setting the fragment size limits is
+ * necessary. Otherwise the driver should not care about them.
+ *
+ * Both the upper and lower limits are set using the same call.
+ * Value of 0 means no limit.
+ *
+ * Here we set the upper limit to PAGE_SIZE and no lower limit.
+ */
+ ossddk_adev_set_buflimits (dev, 0, PAGE_SIZE);
+#endif
+
+#if 0
+ /*
+ * In some cases the device may be rate clocked with some other
+ * device that provides the sample clock. For example there may
+ * be some special cable connected between the devices. When the
+ * driver knows this it can report the device which controls
+ * the sampling rate. Otherwise this information must be left
+ * untouched.
+ *
+ * Knowing that two or more devices have their rates locked will
+ * make work of some future OSS subsystems more reliable. However
+ * false information will probably break things in the hard way.
+ */
+ ossddk_adev_set_ratesource (dev, devc->some_known_device_number);
+#endif
+
+#if 0
+ /*
+ * If the device requires some kind of proprietary control program
+ * it's necessary to assign some magic number for the device.
+ * The control program can find the right device by looking at
+ * the magic field returned by SNDCTL_AUDIOINFO.
+ *
+ * Normal devices must not report any kind of magic numbers. In
+ * particular this feature must not be used to help "client"
+ * programs to detect what kind of device is being used.
+ *
+ * CAUTION! To avoid conflicts between two drivers using the same
+ * magic it's necessary to request the magic number from
+ * 4Front Technologies (support@opensound.com).
+ */
+ ossddk_adev_set_magic (dev, magic_number_assigned_for_this_driver);
+#endif
+ }
+
+ return 0;
+}
diff --git a/misc/samples/ddksample/ddksample_misc.c b/misc/samples/ddksample/ddksample_misc.c
new file mode 100644
index 0000000..8876a2c
--- /dev/null
+++ b/misc/samples/ddksample/ddksample_misc.c
@@ -0,0 +1,179 @@
+/*
+ * ddksample_audio.c - OSS DDK sample driver - misc routines
+ *
+ * Description:
+ * This file contains routines that are not related with the
+ * OSS DDK interface. To understand how OSS DDK works you don't need to
+ * pay any attention on these routines. They are here just to produce
+ * some data for the mixer routines (ddksample_mixer.c).
+ *
+ * ddksample_misc.c emulates some arbitrary audio device. It does
+ * volume scaling to the output data and computes the peak meters from the
+ * result of the scaling.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/*
+ * Solaris DDI includes
+ */
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+/*
+ * OSS specific includes
+ */
+#include <sys/soundcard.h>
+#include <sys/ossddk/ossddk.h>
+
+#include "ddksample.h"
+
+/**************************************************/
+void
+ddksample_do_math (ddksample_portc * portc, void *buf, int len)
+{
+ int i;
+ ddksample_devc *devc = ossddk_adev_get_devc (portc->dev);
+
+ switch (portc->bits + portc->channels)
+ {
+ case 16 + 1: /* 16 bits / mono */
+ {
+ int val;
+ int peak = 0;
+ short *p = buf;
+
+ len /= sizeof (*p);
+
+ for (i = 0; i < len; i++)
+ {
+ /* Do volume computations */
+
+ val = *p++ << 8; /* Scale up to 24 bits */
+
+ val = (val * portc->left_volume) / DDKSAMPLE_MAX_VOL;
+
+ /*
+ * Note that legacy mixer volume max is always
+ * 100.
+ */
+ val = (val * devc->mainvol_left) / 100;
+
+ /*
+ * Now we have the sample value after volume control.
+ * This driver doesn't store this value anywhere but
+ * if necessary this functionality can be added here.
+
+ /*
+ * Next compute the peak value
+ */
+
+ if (val < 0)
+ val = -val; /* Absolute value */
+
+ if (val > peak)
+ peak = val;
+ }
+
+ if (peak > portc->left_peak)
+ portc->left_peak = peak;
+ if (peak > portc->right_peak)
+ portc->right_peak = peak;
+ }
+ break;
+
+ case 16 + 2: /* 16 bits / stereo */
+ {
+ int val;
+ int left_peak = 0, right_peak = 0;
+ short *p = buf;
+
+ len /= sizeof (*p);
+
+ for (i = 0; i < len; i += 2) /* Each stereo sa,ple pair */
+ {
+ /*
+ * Left channel
+ */
+
+ /* Do volume computations */
+
+ val = (*p++) << 8; /* Scale up to 24 bits */
+
+ val = (val * portc->left_volume) / DDKSAMPLE_MAX_VOL;
+
+ /*
+ * Note that legacy mixer volume max is always
+ * 100.
+ */
+ val = (val * devc->mainvol_left) / 100;
+
+ /*
+ * Now we have the sample value after volume control.
+ * This driver doesn't store this value anywhere but
+ * if necessary this functionality can be added here.
+
+ /*
+ * Next compute the peak value
+ */
+
+ if (val < 0)
+ val = -val; /* Absolute value */
+
+ if (val > left_peak)
+ left_peak = val;
+
+ /*
+ * Right channel
+ */
+
+ /* Do volume computations */
+
+ val = (*p++) << 8; /* Scale up to 24 bits */
+
+ val = (val * portc->left_volume) / DDKSAMPLE_MAX_VOL;
+
+ /*
+ * Note that legacy mixer volume max is always
+ * 100.
+ */
+ val = (val * devc->mainvol_left) / 100;
+
+ /*
+ * Now we have the sample value after volume control.
+ * This driver doesn't store this value anywhere but
+ * if necessary this functionality can be added here.
+
+ /*
+ * Next compute the peak value
+ */
+
+ if (val < 0)
+ val = -val; /* Absolute value */
+
+ if (val > right_peak)
+ right_peak = val;
+ }
+
+ if (left_peak > portc->left_peak)
+ portc->left_peak = left_peak;
+ if (right_peak > portc->right_peak)
+ portc->right_peak = right_peak;
+ }
+ break;
+ }
+}
diff --git a/misc/samples/ddksample/ddksample_mixer.c b/misc/samples/ddksample/ddksample_mixer.c
new file mode 100644
index 0000000..3dc5518
--- /dev/null
+++ b/misc/samples/ddksample/ddksample_mixer.c
@@ -0,0 +1,375 @@
+/*
+ * ddksample_audio.c: OSS DDK sample driver - Mixer functionality
+ *
+ * Description:
+ * This source module implements the mixer functionality of the sample
+ * driver. The devc and portc fields used by the mixer are actually used
+ * in the ddksample_misc.c module which emulates some audio hardware.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+
+/*
+ * Solaris DDI includes
+ */
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+/*
+ * OSS specific includes
+ */
+#include <sys/soundcard.h>
+#include <sys/ossddk/ossddk.h>
+
+/***************************************************
+ * Private types and variables
+ */
+
+#include "ddksample.h"
+
+/**************************************************/
+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,
+};
+
+static int
+peak_cb (int dev, int ctl_id, unsigned int cmd, int value)
+{
+ ddksample_devc *devc = ossddk_mixer_get_devc (dev);
+
+ if (ctl_id < 0 || ctl_id >= devc->num_portc)
+ return -EINVAL;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ ddksample_portc *portc = &devc->portc[ctl_id];
+ int val, left, right;
+
+ /*
+ * Truncate the peak counters to 8 bits (signed range).
+ */
+ left = portc->left_peak >> 15;
+ right = portc->right_peak >> 15;
+
+ if (left > 255)
+ left = 255;
+ if (right > 255)
+ right = 255;
+
+ /*
+ * Convert to logarithmic (dB) scale
+ */
+ left = peak_cnv[left];
+ right = peak_cnv[right];
+
+ val = left | (right << 8);
+
+ portc->left_peak = portc->right_peak = 0; /* Reset */
+
+ return val;
+ }
+
+
+ return -EINVAL;
+}
+
+static int
+mute_cb (int dev, int ctl_id, unsigned int cmd, int value)
+{
+ ddksample_devc *devc = ossddk_mixer_get_devc (dev);
+
+ if (ctl_id < 0 || ctl_id >= devc->num_portc)
+ return -EINVAL;
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ ddksample_portc *portc = &devc->portc[ctl_id];
+
+ return portc->mute = (value != 0);
+ }
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ ddksample_portc *portc = &devc->portc[ctl_id];
+
+ return portc->mute;
+ }
+
+ return -EINVAL;
+}
+
+static int
+gain_cb (int dev, int ctl_id, unsigned int cmd, int value)
+{
+ ddksample_devc *devc = ossddk_mixer_get_devc (dev);
+
+ if (ctl_id < 0 || ctl_id >= devc->num_portc)
+ return -EINVAL;
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ int left, right;
+ ddksample_portc *portc = &devc->portc[ctl_id];
+
+ left = value & 0xff;
+ right = (value >> 8) & 0xff;
+
+ if (left < 0)
+ left = 0;
+ if (left > DDKSAMPLE_MAX_VOL)
+ left = DDKSAMPLE_MAX_VOL;
+ if (right < 0)
+ right = 0;
+ if (right > DDKSAMPLE_MAX_VOL)
+ right = DDKSAMPLE_MAX_VOL;
+
+ portc->left_volume = left;
+ portc->right_volume = right;
+
+ return left | (right << 8);
+ }
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ ddksample_portc *portc = &devc->portc[ctl_id];
+ return portc->left_volume | (portc->right_volume << 8);
+ }
+
+ return -EINVAL;
+}
+
+static int
+ddksample_create_controls (int dev)
+{
+ ddksample_devc *devc = ossddk_mixer_get_devc (dev);
+ int group, top, i;
+ int ctl;
+
+ /*
+ * Create the top level group
+ *
+ * By convention all controls specific to /dev/dsp# device files
+ * are grouped under a group called "/dev".
+ */
+
+ if ((top = ossddk_mixer_create_group (dev, // Mixer device number
+ OSSDDK_MIXER_ROOT, // Parent group
+ "/dev")) < 0) // Name
+ return top;
+
+ for (i = 0; i < devc->num_portc; i++)
+ {
+ char name[16];
+
+ ddksample_portc *portc = &devc->portc[i];
+
+ /*
+ * Create a special group for each /dev/dsp# device.
+ * Control/group names staring with "@pcm" have special function.
+ * GUI mixers will replace them with the application name using
+ * the corresponding /dev/dsp# device. In this way the mixer
+ * interface is more intuitive.
+ *
+ * In our case we create a group for each audio device and
+ * place the controls under them.
+ *
+ * We have both a volume slider and a peak meter for each device
+ * file. The convention is that the volume slider will be shown first
+ * if it affects the peak meter (such as in case). Otherwise the
+ * peak meter will be first.
+ */
+ sprintf (name, "@pcm%d", portc->dev);
+
+ if ((group = ossddk_mixer_create_group (dev, // Mixer device number
+ top, // Parent group
+ name)) < 0) // Name
+ return group;
+
+ if ((ctl = ossddk_mixer_create_control (dev, // Mixer device number
+ group, // Parent group
+ i, // Controller id
+ gain_cb, // Callback function
+ MIXT_STEREOSLIDER, // Control type
+ "-", // Name (no name)
+ DDKSAMPLE_MAX_VOL, // Maximum value
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0) // Protection
+ return ctl;
+
+ if ((ctl = ossddk_mixer_create_control (dev, // Mixer device number
+ group, // Parent group
+ i, // Controller id
+ peak_cb, // Callback function
+ MIXT_STEREOPEAK, // Control type
+ "-", // Name (no name)
+ 144, // Maximum value
+ MIXF_READABLE)) < 0) // Protection
+ return ctl;
+
+ if ((ctl = ossddk_mixer_create_control (dev, // Mixer device number
+ group, // Parent group
+ i, // Controller id
+ mute_cb, // Callback function
+ MIXT_ONOFF, // Control type
+ "MUTE", // Name
+ 2, // ONOFF controls should have 2 here
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0) // Protection
+ return ctl;
+ }
+
+ return OSSDDK_OK;
+}
+
+/**************************************************/
+
+static int
+ddksample_legacy_mixer_ioctl (int dev, int audiodev, unsigned int cmd,
+ int *arg)
+{
+ int vol, left, right;
+ ddksample_devc *devc = ossddk_mixer_get_devc (dev);
+
+ switch (cmd)
+ {
+ case SOUND_MIXER_READ_DEVMASK:
+ case SOUND_MIXER_READ_STEREODEVS:
+ *arg = SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_RECLEV;
+ return OSSDDK_OK;
+ break;
+
+ case SOUND_MIXER_READ_RECMASK:
+ case SOUND_MIXER_READ_RECSRC:
+ case SOUND_MIXER_WRITE_RECSRC:
+ *arg = SOUND_MASK_MIC;
+ return OSSDDK_OK;
+ break;
+
+ case SOUND_MIXER_READ_VOLUME:
+ *arg = devc->mainvol_left | (devc->mainvol_right << 8);
+ return OSSDDK_OK;
+ break;
+
+ case SOUND_MIXER_WRITE_VOLUME:
+ vol = *arg;
+ left = vol & 0xff;
+ right = (vol >> 8) & 0xff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ devc->mainvol_left = left;
+ devc->mainvol_right = right;
+
+ *arg = devc->mainvol_left | (devc->mainvol_right << 8);
+ return OSSDDK_OK;
+ break;
+
+ case SOUND_MIXER_READ_MIC:
+ case SOUND_MIXER_WRITE_MIC:
+ *arg = 100 | (100 << 8); /* Always full volume */
+ return OSSDDK_OK;
+ break;
+
+ case SOUND_MIXER_READ_RECLEV:
+ case SOUND_MIXER_WRITE_RECLEV:
+ *arg = 100 | (100 << 8); /* Always full volume */
+ return OSSDDK_OK;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static mixer_driver_t ddksample_mixer_driver = {
+ ddksample_legacy_mixer_ioctl,
+ MAX_SUBDEVICE * 4, /* Number of mixer elements we need */
+ ddksample_create_controls
+};
+
+int
+ddksample_mixer_init (ddksample_devc * devc)
+{
+ devc->mainvol_left = 100;
+ devc->mainvol_right = 100;
+
+ if ((devc->mixer_dev = ossddk_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ "OSS DDK sample mixer",
+ &ddksample_mixer_driver,
+ sizeof (mixer_driver_t),
+ devc)) >= 0)
+ return devc->mixer_dev; /* Error code */
+
+#if 0
+ /*
+ * By default OSS will delay creation of the mixer controls
+ * until the mixer is actually used by some application. This means
+ * that in some cases the mixer doesn't get created at all before
+ * the driver gets unloaded again.
+ *
+ * The driver can force the mixer to be created and the creation
+ * callback (ddksample_create_controls in our case) to be called
+ * immediately by calling ossddk_mixer_touch(). This is necessary only
+ * if the creation function performs initializations that must be
+ * done immediately. Otherwise the touch_mixer() routine must not be
+ * called.
+ *
+ * In this driver ossddk_mixer_touch() must not be called. Mixer
+ * initialization is designed to be invoked after audio devices are
+ * initialized. Otherwise devc->num_portc will not have right value
+ * and mixer initialization will fail.
+ */
+ ossddk_mixer_touch (devc->mixer_dev);
+#endif
+
+ return OSSDDK_OK;
+}