diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /misc/samples | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'misc/samples')
-rw-r--r-- | misc/samples/ddksample/Makefile | 18 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample.c | 282 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample.conf | 1 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample.h | 49 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample_audio.c | 524 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample_misc.c | 179 | ||||
-rw-r--r-- | misc/samples/ddksample/ddksample_mixer.c | 375 |
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; +} |