diff options
Diffstat (limited to 'setup/Linux/oss/cuckoo')
-rw-r--r-- | setup/Linux/oss/cuckoo/Makefile | 23 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/checksum.h | 0 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/cuckoo.c | 251 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/cuckoo.h | 171 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/cuckoo_midi.c | 310 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/cuckoo_mixer.c | 389 | ||||
-rw-r--r-- | setup/Linux/oss/cuckoo/cuckoo_pcm.c | 810 |
7 files changed, 1954 insertions, 0 deletions
diff --git a/setup/Linux/oss/cuckoo/Makefile b/setup/Linux/oss/cuckoo/Makefile new file mode 100644 index 0000000..da9013b --- /dev/null +++ b/setup/Linux/oss/cuckoo/Makefile @@ -0,0 +1,23 @@ +ccflags-y += -I/usr/lib/oss + +ifneq ($(KERNELRELEASE),) + + obj-m := cuckoo.o cuckoo_pcm.o cuckoo_mixer.o + +else + + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + +install: default + cp *.ko /lib/modules/`uname -r`/kernel/oss + depmod -a + +clean: + rm -f *.o *.ko *.mod.c *.mod.o .*.cmd core core.* x y z + rm -rf .tmp_versions Modules.symvers diff --git a/setup/Linux/oss/cuckoo/checksum.h b/setup/Linux/oss/cuckoo/checksum.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/setup/Linux/oss/cuckoo/checksum.h diff --git a/setup/Linux/oss/cuckoo/cuckoo.c b/setup/Linux/oss/cuckoo/cuckoo.c new file mode 100644 index 0000000..b7a04a6 --- /dev/null +++ b/setup/Linux/oss/cuckoo/cuckoo.c @@ -0,0 +1,251 @@ +/* + * This software module makes it possible to use Open Sound System for Linux + * (the _professional_ version) as a low level driver source for ALSA. + * + * Copyright (C) 2004-2009 Hannu Savolainen (hannu@opensound.com). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +/* + * !!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!! + * + * If this file doesn't compile, you must not try to resolve the problem + * without perfect understanding of internals of Linux kernel, ALSA and + * Open Sound System. + * + * Instead you need to check that you are using the version of this file + * that matches the versions of ALSA, OSS and Linux you are currently using. + */ + +#ifndef KBUILD_MODNAME +#define KBUILD_MODNAME cuckoo +#endif + +#include "cuckoo.h" + +#include "./checksum.h" + +#ifdef VERMAGIC_STRING +static const char vermagic[] = VERMAGIC_STRING; +#endif + +MODULE_AUTHOR ("Hannu Savolainen <hannu@opensound.com>"); +MODULE_LICENSE ("GPL v2"); +//MODULE_CLASSES("{sound}"); +MODULE_DESCRIPTION ("OSS low level driver interface for ALSA"); + +#define CUCKOO_MAXCARD SNDRV_CARDS +static int index[CUCKOO_MAXCARD] = SNDRV_DEFAULT_IDX; +static char *id[CUCKOO_MAXCARD] = SNDRV_DEFAULT_STR; +static int enable[CUCKOO_MAXCARD] = SNDRV_DEFAULT_ENABLE_PNP; + +static int +snd_cuckoo_free (cuckoo_t * chip) +{ + // TODO + return 0; +} + +static int +snd_cuckoo_dev_free (snd_device_t * device) +{ + cuckoo_t *cuckoo = (cuckoo_t *) device->device_data; + return snd_cuckoo_free (cuckoo); +} + +static int +snd_cuckoo_create (snd_card_t * card, int osscard, cuckoo_t ** rchip) +{ + cuckoo_t *chip; + int err; + + static snd_device_ops_t ops = { + .dev_free = snd_cuckoo_dev_free + }; + + *rchip = NULL; + + if ((chip = (cuckoo_t *) kmalloc (sizeof (cuckoo_t), GFP_KERNEL)) == NULL) + return -ENOMEM; + + chip->card = card; + chip->osscard = osscard; + chip->ncapture = chip->nplay = chip->npcm = 0; + + if ((err = snd_device_new (card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + { + snd_cuckoo_free (chip); + return err; + } + + *rchip = chip; + return 0; +} + +static snd_card_t *cards[SNDRV_CARDS]; +static int ncards = 0; + +int +init_module (void) +{ + int err; + int dev, cardno; + char tmp[100]; + int pass; + +#if 0 + // TODO + if ((err = udi_connect (WRAPPER_VERSION)) < 0) + return err; + + if (strcmp (oss_checksum, cuckoo_checksum) != 0) + { + printk + ("cuckoo: Error OSS incompatibility problem. Please recompile.\n"); + return -EIO; + } +#endif + + for (pass = 0; pass < 2; pass++) + { + cardno = -1; + + for (dev = 0; dev < num_audio_engines; dev++) + { + adev_p adev = audio_engines[dev]; + cuckoo_t *chip; + snd_card_t *card = NULL; + + if (pass == 0) + { + // Ignore non-virtual devices + if (!(adev->flags & ADEV_VIRTUAL)) + continue; + } + else + { + // Ignore virtual devices + if ((adev->flags & ADEV_VIRTUAL)) + continue; + } + + if (adev->card_number < 0) + { + printk ("cuckoo: Ignored audio device %d - %s\n", dev, + adev->name); + continue; + } + + if (adev->card_number != cardno) + { + oss_card_info cd; + + cardno = adev->card_number; + + if (oss_get_cardinfo (cardno, &cd) < 0) + { + printk ("oss_get_cardinfo(%d) failed\n", cardno); + continue; + } + + // printk("Card %d: %s/%s\n", cardno, cd.shortname, cd.longname); + printk ("Card %d: %s/%s\n", cardno, cd.shortname, cd.longname); + + if (ncards >= CUCKOO_MAXCARD) + { + printk + ("Cuckoo: Too many audio devices (%d), only %d supported. by ALSA.\n", + num_audio_engines, CUCKOO_MAXCARD); + return -EIO; + } + + if (!enable[ncards]) + { + printk ("cuckoo: Device was not enabled (yet)\n"); + return -EIO; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31) + if ((card = + snd_card_new (index[ncards], id[ncards], THIS_MODULE, + 0)) == NULL) +#else + if ( + snd_card_create (index[ncards], id[ncards], THIS_MODULE, + 0, &card) != 0) +#endif + { + printk ("cuckoo: Can't create a card instance\n"); + return -EIO; + } + + if ((err = snd_cuckoo_create (card, cardno, &chip)) < 0) + { + printk ("cuckoo: Couldn't create a chip instance (%d)\n", + err); + snd_card_free (card); + return err; + } + +#define oss_version_string "v4.x" // TODO + sprintf (tmp, "OSS %s", oss_version_string); + strlcpy (card->driver, tmp); + strlcpy (card->shortname, cd.shortname); + strlcpy (card->longname, cd.longname); + + if ((err = install_pcm_instances (chip, cardno)) < 0) + return err; + + if ((err = install_mixer_instances (chip, cardno)) < 0) + return err; + + // if ((err=install_midiport_instances(chip, cardno))<0) + // return err; + + if ((err = snd_card_register (card)) < 0) + { + printk ("cuckoo: Couldn't register card(%s) err=%d\n", + card->shortname, err); + continue; // TODO: Should handle this in more intelligent way + + snd_card_free (card); + return err; + } + + cards[ncards++] = card; + } + } + } + + return 0; +} + +void +cleanup_module (void) +{ + int i; + + for (i = 0; i < ncards; i++) + snd_card_free (cards[i]); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,5)) +#undef unix +struct module __this_module + __attribute__ ((section (".gnu.linkonce.this_module"))) = +{ + .name = __stringify (KBUILD_MODNAME),.init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module +#endif +}; +#endif diff --git a/setup/Linux/oss/cuckoo/cuckoo.h b/setup/Linux/oss/cuckoo/cuckoo.h new file mode 100644 index 0000000..3df2966 --- /dev/null +++ b/setup/Linux/oss/cuckoo/cuckoo.h @@ -0,0 +1,171 @@ +/* + * This software module makes it possible to use Open Ssund System for Linux + * (the _professional_ version) as a low level driver source for ALSA. + * + * Copyright (C) 2004-2006 Hannu Savolainen (hannu@voimakentta.net). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#define _KERNEL + +/* + * !!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!! + * + * If this file doesn't compile, you must not try to resolve the problem + * without perfect understanding of internals of Linux kernel, ALSA and + * Open Sound System. + * + * Instead you need to check that you are using the version of this file + * that matches the versions of ALSA, OSS and Linux you are currently using. + */ + +#define _KERNEL +#include "../include/sys/soundcard.h" + +#include <linux/version.h> + +#define _LOOSE_KERNEL_NAMES + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18) +#include <linux/config.h> +#else +#include <linux/autoconf.h> +#endif + +#if !defined(__SMP__) && defined(CONFIG_SMP) +#define __SMP__ +#endif +#include <linux/module.h> + +#include <stdarg.h> + +extern int oss_get_cardinfo (int cardnum, oss_card_info * ci); /* from oss_config.h */ + +#include <linux/param.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <linux/pci.h> +#include <linux/apm_bios.h> +#include <asm/segment.h> +#include <asm/uaccess.h> +#include <linux/poll.h> + +#include <asm/system.h> +#include <asm/dma.h> +#include <linux/wait.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/ioport.h> +//#include <asm/mach-default/irq_vectors.h> +#include <linux/interrupt.h> +#include <linux/pm.h> + +struct _oss_mutex_t +{ + /* Caution! This definition must match Linux/osscore.c */ + spinlock_t lock; +}; + +#define audio_devs dummy_audio_devs + +#include "../include/internals/oss_exports.h" +#include "../build/wrap.h" +#include "../include/internals/ossddk.h" + +typedef struct oss_wait_queue oss_wait_queue_t; /* This must match oss_config.h */ + +#include "../include/internals/ossddk.h" + +//#include <sound/driver.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> + +#include "../build/osscore_symbols.inc" + +#define SNDRV_GET_ID +#include <sound/initval.h> + +typedef caddr_t ioctl_arg; +typedef char snd_rw_buf; + +typedef int sound_os_info; + +#define WR_BUF_CONST const + +#include "../include/internals/audio_core.h" +#include "../include/internals/mixer_core.h" + +typedef struct _snd_cuckoo cuckoo_t, chip_t; + +typedef struct +{ + adev_p adev; +} cuckoo_pcm_t; + +#define MAX_OSSPCM 24 // Max # of PCM devices/card instance + +#if 1 +// Older ALSA versions used to define these... +typedef struct snd_card snd_card_t; +typedef struct snd_pcm snd_pcm_t; +typedef struct snd_rawmidi snd_rawmidi_t; +typedef struct snd_rawmidi_substream snd_rawmidi_substream_t; +typedef struct snd_rawmidi_ops snd_rawmidi_ops_t; +typedef struct snd_kcontrol snd_kcontrol_t; +typedef struct snd_kcontrol_new snd_kcontrol_new_t; +typedef struct snd_ctl_elem_info snd_ctl_elem_info_t; +typedef struct snd_ctl_elem_value snd_ctl_elem_value_t; +typedef struct snd_pcm_substream snd_pcm_substream_t; +typedef struct snd_pcm_hardware snd_pcm_hardware_t; +typedef struct snd_pcm_runtime snd_pcm_runtime_t; +typedef struct snd_pcm_hw_params snd_pcm_hw_params_t; +typedef struct snd_pcm_ops snd_pcm_ops_t; +typedef struct snd_device snd_device_t; +typedef struct snd_device_ops snd_device_ops_t; +#endif + +struct _snd_cuckoo +{ + snd_card_t *card; + snd_pcm_t *pcm[MAX_OSSPCM]; + adev_p play_adev[MAX_OSSPCM], capture_adev[MAX_OSSPCM]; + int osscard; + int nplay, ncapture, npcm; +}; + +#define cuckoo_t_magic 0xaabbccdd +#define chip__tmagic cuckoo_t_magic + +//#define OPEN_READ PCM_ENABLE_INPUT +//#define OPEN_WRITE PCM_ENABLE_OUTPUT + +extern int install_mixer_instances (cuckoo_t * chip, int cardno); +extern int install_midiport_instances (cuckoo_t * chip, int cardno); +extern int install_pcm_instances (cuckoo_t * chip, int cardno); + +// Disable locking for now +#define udi_spin_lock_irqsave(a, b) *(b)=0 +#define udi_spin_unlock_irqrestore(a, b) + +#define strlcpy(a, b) {strncpy(a, b, sizeof(a)-1);a[sizeof(a)-1]=0;} diff --git a/setup/Linux/oss/cuckoo/cuckoo_midi.c b/setup/Linux/oss/cuckoo/cuckoo_midi.c new file mode 100644 index 0000000..949379b --- /dev/null +++ b/setup/Linux/oss/cuckoo/cuckoo_midi.c @@ -0,0 +1,310 @@ +/* + * This software module makes it possible to use Open Sound System for Linux + * (the _professional_ version) as a low level driver source for ALSA. + * + * Copyright (C) 2004 Hannu Savolainen (hannu@voimakentta.net). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +/* + * !!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!! + * + * If this file doesn't compile, you must not try to resolve the problem + * without perfect understanding of internals of Linux kernel, ALSA and + * Open Sound System. + * + * Instead you need to check that you are using the version of this file + * that matches the versions of ALSA, OSS and Linux you are currently using. + */ + +#include "cuckoo.h" +#include <sound/rawmidi.h> +#include <midi_core.h> + +static snd_rawmidi_t *rmidis[256] = { NULL }; + +static int +cuckoo_uart_input_open (snd_rawmidi_substream_t * substream) +{ +#if 0 + mpu401_t *mpu; + int err; + + mpu = + snd_magic_cast (mpu401_t, substream->rmidi->private_data, return -ENXIO); + if (mpu->open_input && (err = mpu->open_input (mpu)) < 0) + return err; + if (!test_bit (MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + { + cuckoo_uart_cmd (mpu, MPU401_RESET, 1); + cuckoo_uart_cmd (mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_input = substream; + atomic_set (&mpu->rx_loop, 1); + set_bit (MPU401_MODE_BIT_INPUT, &mpu->mode); +#endif + return 0; +} + +static int +cuckoo_uart_output_open (snd_rawmidi_substream_t * substream) +{ + int dev; + + dev = (int) substream->rmidi->private_data; + + printk ("Output open %d\n", dev); + + return -EIO; +#if 0 + mpu401_t *mpu; + int err; + if (mpu->open_output && (err = mpu->open_output (mpu)) < 0) + return err; + if (!test_bit (MPU401_MODE_BIT_INPUT, &mpu->mode)) + { + cuckoo_uart_cmd (mpu, MPU401_RESET, 1); + cuckoo_uart_cmd (mpu, MPU401_ENTER_UART, 1); + } + mpu->substream_output = substream; + atomic_set (&mpu->tx_loop, 1); + set_bit (MPU401_MODE_BIT_OUTPUT, &mpu->mode); +#endif + return 0; +} + +static int +cuckoo_uart_input_close (snd_rawmidi_substream_t * substream) +{ +#if 0 + mpu401_t *mpu; + + mpu = + snd_magic_cast (mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit (MPU401_MODE_BIT_INPUT, &mpu->mode); + mpu->substream_input = NULL; + if (!test_bit (MPU401_MODE_BIT_OUTPUT, &mpu->mode)) + cuckoo_uart_cmd (mpu, MPU401_RESET, 0); + if (mpu->close_input) + mpu->close_input (mpu); +#endif + return 0; +} + +static int +cuckoo_uart_output_close (snd_rawmidi_substream_t * substream) +{ +#if 0 + mpu401_t *mpu; + + mpu = + snd_magic_cast (mpu401_t, substream->rmidi->private_data, return -ENXIO); + clear_bit (MPU401_MODE_BIT_OUTPUT, &mpu->mode); + mpu->substream_output = NULL; + if (!test_bit (MPU401_MODE_BIT_INPUT, &mpu->mode)) + cuckoo_uart_cmd (mpu, MPU401_RESET, 0); + if (mpu->close_output) + mpu->close_output (mpu); +#endif + return 0; +} + +static void +cuckoo_uart_input_trigger (snd_rawmidi_substream_t * substream, int up) +{ +#if 0 + unsigned long flags; + mpu401_t *mpu; + int max = 64; + + mpu = snd_magic_cast (mpu401_t, substream->rmidi->private_data, return); + if (up) + { + if (!test_and_set_bit (MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) + { + /* first time - flush FIFO */ + while (max-- > 0) + mpu->read (mpu, MPU401D (mpu)); + if (mpu->irq < 0) + cuckoo_uart_add_timer (mpu, 1); + } + + /* read data in advance */ + /* prevent double enter via rawmidi->event callback */ + if (atomic_dec_and_test (&mpu->rx_loop)) + { + local_irq_save (flags); + if (spin_trylock (&mpu->input_lock)) + { + cuckoo_uart_input_read (mpu); + spin_unlock (&mpu->input_lock); + } + local_irq_restore (flags); + } + atomic_inc (&mpu->rx_loop); + } + else + { + if (mpu->irq < 0) + cuckoo_uart_remove_timer (mpu, 1); + clear_bit (MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode); + } +#endif +} + +#if 0 +static void +cuckoo_uart_input_read (void *mpu) +{ + int max = 128; + unsigned char byte; + + while (max-- > 0) + { + if (cuckoo_input_avail (mpu)) + { + byte = mpu->read (mpu, MPU401D (mpu)); + if (test_bit (MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) + snd_rawmidi_receive (mpu->substream_input, &byte, 1); + } + else + { + break; /* input not available */ + } + } +} +#endif + +#if 0 +static void +cuckoo_uart_output_write (void *mpu) +{ + unsigned char byte; + int max = 256, timeout; + + do + { + if (snd_rawmidi_transmit_peek (mpu->substream_output, &byte, 1) == 1) + { + for (timeout = 100; timeout > 0; timeout--) + { + if (cuckoo_output_ready (mpu)) + { + mpu->write (mpu, byte, MPU401D (mpu)); + snd_rawmidi_transmit_ack (mpu->substream_output, 1); + break; + } + } + if (timeout == 0) + break; /* Tx FIFO full - try again later */ + } + else + { + cuckoo_uart_remove_timer (mpu, 0); + break; /* no other data - leave the tx loop */ + } + } + while (--max > 0); +} +#endif + +static void +cuckoo_uart_output_trigger (snd_rawmidi_substream_t * substream, int up) +{ + int dev; + + dev = (int) substream->rmidi->private_data; + printk ("Output trigger %d\n", dev); +#if 0 + if (up) + { + set_bit (MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + + /* try to add the timer at each output trigger, + * since the output timer might have been removed in + * cuckoo_uart_output_write(). + */ + cuckoo_uart_add_timer (mpu, 0); + + /* output pending data */ + /* prevent double enter via rawmidi->event callback */ + if (atomic_dec_and_test (&mpu->tx_loop)) + { + local_irq_save (flags); + if (spin_trylock (&mpu->output_lock)) + { + cuckoo_uart_output_write (mpu); + spin_unlock (&mpu->output_lock); + } + local_irq_restore (flags); + } + atomic_inc (&mpu->tx_loop); + } + else + { + cuckoo_uart_remove_timer (mpu, 0); + clear_bit (MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); + } +#endif +} + +static snd_rawmidi_ops_t cuckoo_uart_output = { + .open = cuckoo_uart_output_open, + .close = cuckoo_uart_output_close, + .trigger = cuckoo_uart_output_trigger, +}; + +static snd_rawmidi_ops_t cuckoo_uart_input = { + .open = cuckoo_uart_input_open, + .close = cuckoo_uart_input_close, + .trigger = cuckoo_uart_input_trigger, +}; + +extern mididev_p *midi_devs; + +int +install_midiport_instances (cuckoo_t * chip, int cardno) +{ + int dev, devix = 0; + + for (dev = 0; dev < num_mididevs; dev++) + if (midi_devs[dev]->card_number == cardno) + { + mididev_p mididev = midi_devs[dev]; + snd_rawmidi_t *rmidi; + int err; + +//printk("Midi device %s\n", mididev->info.name); + + if ((err = snd_rawmidi_new (chip->card, mididev->name, devix, + 1, 1, &rmidi)) < 0) + { + printk ("cuckoo: Failed to register rawmidi device, err=%d\n", + err); + return 0; + } + + rmidi->private_data = (void *) dev; + strcpy (rmidi->name, mididev->name); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + snd_rawmidi_set_ops (rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &cuckoo_uart_output); + snd_rawmidi_set_ops (rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &cuckoo_uart_input); + + devix++; + rmidis[dev] = rmidi; + } // dev + + return 0; +} diff --git a/setup/Linux/oss/cuckoo/cuckoo_mixer.c b/setup/Linux/oss/cuckoo/cuckoo_mixer.c new file mode 100644 index 0000000..6d2bb6d --- /dev/null +++ b/setup/Linux/oss/cuckoo/cuckoo_mixer.c @@ -0,0 +1,389 @@ +/* + * This software module makes it possible to use Open Sound System for Linux + * (the _professional_ version) as a low level driver source for ALSA. + * + * Copyright (C) 2004 Hannu Savolainen (hannu@voimakentta.net). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +/* + * !!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!! + * + * If this file doesn't compile, you must not try to resolve the problem + * without perfect understanding of internals of Linux kernel, ALSA and + * Open Sound System. + * + * Instead you need to check that you are using the version of this file + * that matches the versions of ALSA, OSS and Linux you are currently using. + */ + +#include "cuckoo.h" + +MODULE_AUTHOR ("Hannu Savolainen <hannu@opensound.com>"); +MODULE_LICENSE ("GPL v2"); +MODULE_DESCRIPTION ("OSS mixer low level driver interface for ALSA"); + +typedef struct +{ + char *name, *data; +} enum_entry_t; + +#if 0 +static void +downshift (char *s) +{ + while (*s) + { + if (*s >= 'A' && *s <= 'Z') + *s += 32; + s++; + } +} +#endif + +static int +get_mixer_info (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +{ + oss_mixext ext; + int dev, ix; + + dev = ext.dev = kcontrol->private_value >> 16; + ix = ext.ctrl = kcontrol->private_value & 0xffff;; + + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_EXTINFO, (caddr_t) & ext); + + switch (ext.type) + { + case MIXT_STEREOSLIDER: + case MIXT_STEREOVU: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = ext.minvalue; + uinfo->value.integer.max = ext.maxvalue; + break; + + case MIXT_MONOSLIDER: + case MIXT_MONOVU: + case MIXT_SLIDER: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ext.minvalue; + uinfo->value.integer.max = ext.maxvalue; + break; + + case MIXT_ONOFF: + case MIXT_MUTE: + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + break; + + case MIXT_ENUM: + { + static const char *texts[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", + "30", "31", "32" + }; + oss_mixer_enuminfo enumdef; + uinfo->value.enumerated.items = ext.maxvalue; + + if (uinfo->value.enumerated.item < 0) + uinfo->value.enumerated.item = 0; + if (uinfo->value.enumerated.item >= ext.maxvalue) + uinfo->value.enumerated.item = ext.maxvalue - 1; + if (uinfo->value.enumerated.item > 31) + uinfo->value.enumerated.item = 31; + strcpy (uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + enumdef.dev = ext.dev; + enumdef.ctrl = ext.ctrl; + if (oss_mixer_ext + (dev, OSS_DEV_MIXER, SNDCTL_MIX_ENUMINFO, + (caddr_t) & enumdef) >= 0) + { + char *text; + + text = + &enumdef.strings[enumdef. + strindex[uinfo->value.enumerated.item]]; + strcpy (uinfo->value.enumerated.name, text); + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ext.maxvalue; + } + break; + + default: + printk ("cuckoo: mixer_info(%d/%d) - unknown type %d\n", dev, ix, + ext.type); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ext.minvalue; + uinfo->value.integer.max = ext.maxvalue; + return 0; + } + + return 0; +} + +static int +mixer_get (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + oss_mixext ext; + oss_mixer_value val; + int dev, ix, err; + + dev = ext.dev = kcontrol->private_value >> 16; + ix = ext.ctrl = kcontrol->private_value & 0xffff;; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_EXTINFO, + (caddr_t) & ext)) < 0) + return err; + + val.dev = dev; + val.ctrl = ix; + val.timestamp = ext.timestamp; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_READ, + (caddr_t) & val)) < 0) + return err; + + switch (ext.type) + { + case MIXT_STEREOVU: + case MIXT_STEREOSLIDER: + ucontrol->value.integer.value[0] = val.value & 0xff; // Left + ucontrol->value.integer.value[1] = (val.value >> 8) & 0xff; // Right + break; + + case MIXT_MONOSLIDER: + case MIXT_MONOVU: + case MIXT_SLIDER: + ucontrol->value.integer.value[0] = val.value & 0xff; + break; + + case MIXT_ONOFF: + case MIXT_MUTE: + ucontrol->value.integer.value[0] = !!val.value; + break; + + case MIXT_ENUM: + ucontrol->value.integer.value[0] = val.value; + break; + + default: + printk ("cuckoo: mixer_get(%d/%d) - unknown type %d\n", dev, ix, + ext.type); + ucontrol->value.integer.value[0] = val.value & 0xff; + return 0; + } + + return 0; +} + +static int +mixer_put (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + oss_mixext ext; + oss_mixer_value val; + int dev, ix, err; + + dev = ext.dev = kcontrol->private_value >> 16; + ix = ext.ctrl = kcontrol->private_value & 0xffff;; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_EXTINFO, + (caddr_t) & ext)) < 0) + return err; + + val.dev = dev; + val.ctrl = ix; + val.timestamp = ext.timestamp; + + switch (ext.type) + { + case MIXT_STEREOSLIDER: + val.value = ucontrol->value.integer.value[0] | // Left + ucontrol->value.integer.value[1] << 8; // Right + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, + (caddr_t) & val)) < 0) + return err; + break; + + case MIXT_MONOSLIDER: + case MIXT_SLIDER: + val.value = ucontrol->value.integer.value[0]; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, + (caddr_t) & val)) < 0) + return err; + break; + + case MIXT_ONOFF: + case MIXT_MUTE: + val.value = !!ucontrol->value.integer.value[0]; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, + (caddr_t) & val)) < 0) + return err; + break; + + case MIXT_ENUM: + val.value = ucontrol->value.integer.value[0]; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, + (caddr_t) & val)) < 0) + return err; + break; + + case MIXT_MONOVU: + case MIXT_STEREOVU: + return -EPERM; + + default: + printk ("cuckoo: mixer_put(%d/%d) - unknown type %d\n", dev, ix, + ext.type); + val.value = ucontrol->value.integer.value[0]; + if ((err = + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, + (caddr_t) & val)) < 0) + return err; + } + + return 0; +} + +static void +add_control (cuckoo_t * chip, int dev, int ix, oss_mixext * ext, char *name) +{ + int i, ok, err = 0; + snd_kcontrol_new_t my_control; + +// Upshift the name if it's an single part one + + ok = 0; + for (i = 0; i < strlen (name); i++) + if (name[i] == '.') + ok = 1; + if (!ok) + for (i = 0; i < strlen (name); i++) + if (name[i] >= 'a' && name[i] <= 'z') + name[i] -= 32; + +// Add the control + + memset (&my_control, 0, sizeof (my_control)); + + my_control.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + my_control.name = name; + my_control.index = 0; + my_control.access = 0; + + if (ext->flags & MIXF_READABLE) + my_control.access |= SNDRV_CTL_ELEM_ACCESS_READ; + if (ext->flags & MIXF_WRITEABLE) + my_control.access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + if ((ext->flags & 0x3) == MIXF_READABLE) /* Read only */ + my_control.access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + my_control.private_value = (dev << 16) | ix; + my_control.info = get_mixer_info; + my_control.get = mixer_get; + my_control.put = mixer_put; + + switch (ext->type) + { + case MIXT_ENUM: + case MIXT_ONOFF: + case MIXT_MUTE: + case MIXT_STEREOSLIDER: + case MIXT_SLIDER: + case MIXT_MONOSLIDER: + case MIXT_MONOVU: + case MIXT_STEREOVU: + if ((err = + snd_ctl_add (chip->card, snd_ctl_new1 (&my_control, chip))) < 0) + { + printk ("cuckoo: snd_ctl_add(%s) failed, err=%d\n", ext->extname, + err); + return; + } + break; + } +} + +int +install_mixer_instances (cuckoo_t * chip, int cardno) +{ + int dev; + mixer_operations_t **cuckoo_mixer_devs = mixer_devs_p; + + for (dev = 0; dev < num_mixers; dev++) + if (cuckoo_mixer_devs[dev]->card_number == cardno) + { + int nrext, i, sz; + + touch_mixer (dev); + + nrext = dev; + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_NREXT, + (ioctl_arg) & nrext); + + if (nrext == 0) + continue; + + sz = nrext * (sizeof (char *) + 32); // 32 characters / name (average) + + for (i = 0; i < nrext; i++) + { + oss_mixext ext; + int parent = 0; + oss_mixext_root *root = NULL; + + ext.dev = dev; + ext.ctrl = i; + oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_EXTINFO, + (caddr_t) & ext); + + switch (ext.type) + { + case MIXT_DEVROOT: + root = (oss_mixext_root *) & ext.data; + break; + + case MIXT_GROUP: + parent = ext.parent; + break; + + case MIXT_MARKER: + break; + + default: + add_control (chip, dev, i, &ext, ext.extname); + break; + } // Switch + + } // i + + + } // dev + + return 0; +} + +EXPORT_SYMBOL (install_mixer_instances); diff --git a/setup/Linux/oss/cuckoo/cuckoo_pcm.c b/setup/Linux/oss/cuckoo/cuckoo_pcm.c new file mode 100644 index 0000000..2ae08b5 --- /dev/null +++ b/setup/Linux/oss/cuckoo/cuckoo_pcm.c @@ -0,0 +1,810 @@ +/* + * This software module makes it possible to use Open Sound System for Linux + * (the _professional_ version) as a low level driver source for ALSA. + * + * Copyright (C) 2004 Hannu Savolainen (hannu@voimakentta.net). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +/* + * !!!!!!!!!!!!!!!!!!!! Important !!!!!!!!!!!!!!!!!! + * + * If this file doesn't compile, you must not try to resolve the problem + * without perfect understanding of internals of Linux kernel, ALSA and + * Open Sound System. + * + * Instead you need to check that you are using the version of this file + * that matches the versions of ALSA, OSS and Linux you are currently using. + */ + +#include "cuckoo.h" + +MODULE_AUTHOR ("Hannu Savolainen <hannu@opensound.com>"); +MODULE_LICENSE ("GPL v2"); +MODULE_DESCRIPTION ("OSS PCM low level driver interface for ALSA"); + +static snd_pcm_substream_t *cuckoo_playsubstream[256] = { NULL }; +static snd_pcm_substream_t *cuckoo_capturesubstream[256] = { NULL }; + +static snd_pcm_hardware_t snd_cuckoo_playback = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (64 * 1024), + .period_bytes_min = 64, + .period_bytes_max = (32 * 1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_cuckoo_capture = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128 * 1024), + .period_bytes_min = 64, + .period_bytes_max = (64 * 1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void +cuckoo_outputintr (int dev, int notify_only) +{ + snd_pcm_substream_t *substream; + adev_t *adev; + dmap_t *dmap; + oss_native_word flags; + + if (dev < 0 || dev > 255) + return; + + adev = audio_devfiles[dev]; + dmap = adev->dmap_out; + + dmap->fragment_counter = (dmap->fragment_counter + 1) % dmap->nfrags; + + substream = cuckoo_playsubstream[dev]; + if (substream == NULL) + return; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + snd_pcm_period_elapsed (substream); + udi_spin_unlock_irqrestore (&adev->mutex, flags); +} + +static void +cuckoo_inputintr (int dev, int intr_flags) +{ + snd_pcm_substream_t *substream; + adev_t *adev; + dmap_t *dmap; + oss_native_word flags; + + if (dev < 0 || dev > 255) + return; + + adev = audio_devfiles[dev]; + dmap = adev->dmap_in; + + dmap->fragment_counter = (dmap->fragment_counter + 1) % dmap->nfrags; + + substream = cuckoo_capturesubstream[dev]; + if (substream == NULL) + return; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + snd_pcm_period_elapsed (substream); + udi_spin_unlock_irqrestore (&adev->mutex, flags); +} + +static void +copy_hw_caps (snd_pcm_runtime_t * runtime, adev_t * adev, int dir) +{ + u64 fmts = 0; + int i; + unsigned int fmtmask; + + if (dir == OPEN_WRITE) + { + fmtmask = adev->oformat_mask; + } + else + { + fmtmask = adev->iformat_mask; + } + + for (i = 0; i < 32; i++) + switch (fmtmask & (1 << i)) + { + case AFMT_MU_LAW: + fmts |= SNDRV_PCM_FMTBIT_MU_LAW; + break; + case AFMT_A_LAW: + fmts |= SNDRV_PCM_FMTBIT_A_LAW; + break; + case AFMT_IMA_ADPCM: + fmts |= SNDRV_PCM_FMTBIT_IMA_ADPCM; + break; + case AFMT_U8: + fmts |= SNDRV_PCM_FMTBIT_U8; + break; + case AFMT_S8: + fmts |= SNDRV_PCM_FMTBIT_S8; + break; + case AFMT_S16_LE: + fmts |= SNDRV_PCM_FMTBIT_S16_LE; + break; + case AFMT_S16_BE: + fmts |= SNDRV_PCM_FMTBIT_S16_BE; + break; + case AFMT_S24_LE: + fmts |= SNDRV_PCM_FMTBIT_S24_LE; + break; + case AFMT_S24_BE: + fmts |= SNDRV_PCM_FMTBIT_S24_BE; + break; + case AFMT_S32_LE: + fmts |= SNDRV_PCM_FMTBIT_S32_LE; + break; + case AFMT_S32_BE: + fmts |= SNDRV_PCM_FMTBIT_S32_BE; + break; + case AFMT_MPEG: + fmts |= SNDRV_PCM_FMTBIT_MPEG; + break; + case AFMT_FLOAT: + fmts |= SNDRV_PCM_FMTBIT_FLOAT_LE; + break; + case AFMT_SPDIF_RAW: + fmts |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + break; + } + + runtime->hw.formats = fmts; + + if (adev->min_block > 0) + runtime->hw.period_bytes_min = adev->min_block; + if (adev->max_block > 0) + runtime->hw.period_bytes_max = adev->max_block; + + if (adev->flags & ADEV_NOMMAP) + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); + + if (adev->max_rate > adev->min_rate) + { + runtime->hw.rate_min = adev->min_rate; + runtime->hw.rate_max = adev->max_rate; + } + + if (!(adev->caps & DSP_CAP_FREERATE)) + runtime->hw.rates &= + ~(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000); +} + +static int +snd_cuckoo_playback_open (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + int err; + adev_t *adev; + oss_native_word flags; + struct fileinfo tmp_finfo; + + if (snum < 0 || snum >= chip->npcm) + { + printk ("cuckoo: Playback open - bad substream index %d\n", snum); + return -EIO; + } + + adev = chip->play_adev[snum]; + printk ("cuckoo_playback_open(%d=%s)\n", adev->engine_num, adev->name); + + cuckoo_playsubstream[adev->engine_num] = substream; + + if (adev->dmap_out == NULL || adev->dmap_out->dmabuf == NULL) + { + printk ("cuckoo: dev %d - no buffer available\n", adev->engine_num); + return -ENOMEM; + } + + if (adev->open_mode != 0) + { + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return -EBUSY; + } + + tmp_finfo.mode = OPEN_WRITE; + tmp_finfo.acc_flags = 0; + if ((err = + oss_audio_open_engine (adev->engine_num, OSS_DEV_DSP, + &tmp_finfo, 1, OF_SMALLBUF, + NULL)) < 0) + { + return err; + } + + udi_spin_lock_irqsave (&adev->mutex, &flags); + adev->open_mode = OPEN_WRITE; + runtime->hw = snd_cuckoo_playback; + copy_hw_caps (runtime, adev, OPEN_WRITE); + snd_pcm_set_sync (substream); + adev->pid = current->pid; + strncpy (adev->cmd, current->comm, sizeof (adev->cmd) - 1); + adev->cmd[sizeof (adev->cmd) - 1] = 0; + adev->outputintr = cuckoo_outputintr; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + + return 0; +} + +static int +snd_cuckoo_capture_open (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + int err; + adev_t *adev; + oss_native_word flags; + struct fileinfo tmp_finfo; + + if (snum < 0 || snum >= chip->npcm) + { + printk ("cuckoo: Capture open - bad substream index %d\n", snum); + return -EIO; + } + + adev = chip->capture_adev[snum]; + + cuckoo_capturesubstream[adev->engine_num] = substream; + + if (adev->dmap_in == NULL || adev->dmap_in->dmabuf == NULL) + { + printk ("cuckoo: dev %d - no buffer available\n", adev->engine_num); + return -ENOMEM; + } + + if (adev->open_mode != 0) + { + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return -EBUSY; + } + + tmp_finfo.mode = OPEN_READ; + tmp_finfo.acc_flags = 0; + if ((err = + oss_audio_open_engine (adev->engine_num, OSS_DEV_DSP, + &tmp_finfo, 1, OF_SMALLBUF, + NULL)) < 0) + { + return err; + } + + udi_spin_lock_irqsave (&adev->mutex, &flags); + adev->open_mode = OPEN_READ; + runtime->hw = snd_cuckoo_capture; + copy_hw_caps (runtime, adev, OPEN_READ); + snd_pcm_set_sync (substream); + adev->pid = current->pid; + strncpy (adev->cmd, current->comm, sizeof (adev->cmd) - 1); + adev->cmd[sizeof (adev->cmd) - 1] = 0; + adev->inputintr = cuckoo_inputintr; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + + return 0; +} + +static int +snd_cuckoo_playback_close (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + int snum = substream->number; + adev_t *adev; + oss_native_word flags; + struct fileinfo tmp_finfo; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->play_adev[snum]; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + cuckoo_playsubstream[adev->engine_num] = NULL; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + + tmp_finfo.mode = OPEN_WRITE; + tmp_finfo.acc_flags = 0; + oss_audio_release (adev->engine_num, &tmp_finfo); + + return 0; +} + +static int +snd_cuckoo_capture_close (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + int snum = substream->number; + adev_t *adev; + oss_native_word flags; + struct fileinfo tmp_finfo; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->capture_adev[snum]; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + cuckoo_capturesubstream[adev->engine_num] = NULL; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + + tmp_finfo.mode = OPEN_READ; + tmp_finfo.acc_flags = 0; + oss_audio_release (adev->engine_num, &tmp_finfo); + + return 0; +} + +static int +snd_cuckoo_playback_hw_params (snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + dmap_t *dmap; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->play_adev[snum]; + dmap = adev->dmap_out; + + if (dmap->dmabuf == NULL) + return -ENOMEM; + + runtime->dma_area = dmap->dmabuf; + runtime->dma_addr = dmap->dmabuf_phys; + runtime->dma_bytes = dmap->buffsize; + memset (dmap->dmabuf, 0, dmap->buffsize); + + return 0; +} + +static int +snd_cuckoo_capture_hw_params (snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + dmap_t *dmap; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->capture_adev[snum]; + dmap = adev->dmap_in; + + if (dmap->dmabuf == NULL) + return -ENOMEM; + + runtime->dma_area = dmap->dmabuf; + runtime->dma_addr = dmap->dmabuf_phys; + runtime->dma_bytes = dmap->buffsize; + memset (dmap->dmabuf, 0, dmap->buffsize); + + return 0; +} + +static int +snd_cuckoo_hw_free (snd_pcm_substream_t * substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + + return 0; +} + +static int +snd_cuckoo_playback_prepare (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number, err; + adev_t *adev; + oss_native_word flags; + dmap_t *dmap; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->play_adev[snum]; + dmap = adev->dmap_out; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + + adev->d->adrv_set_format (adev->engine_num, + snd_pcm_format_width (runtime->format)); + runtime->channels = + adev->d->adrv_set_channels (adev->engine_num, runtime->channels); + runtime->rate = adev->d->adrv_set_rate (adev->engine_num, runtime->rate); + adev->user_parms.rate = adev->user_parms.rate = runtime->rate; + + dmap->bytes_in_use = snd_pcm_lib_buffer_bytes (substream); + dmap->fragment_size = snd_pcm_lib_period_bytes (substream); + +#if 1 + { + int f, s;; + + f = dmap->fragment_size / 4; + if (f < 128) + f = dmap->fragment_size / 2; + if (f < 128) + f = dmap->fragment_size; + + s = dmap->bytes_in_use; + while (s > f) + s /= 2; + + dmap->fragment_size = s; + } +#endif + + if (adev->max_block > 0 && dmap->fragment_size > adev->max_block) + dmap->fragment_size = adev->max_block; + if (adev->min_block > 0 && dmap->fragment_size < adev->min_block) + dmap->fragment_size = adev->min_block; + if (dmap->fragment_size < 8) + dmap->fragment_size = 8; + dmap->nfrags = dmap->bytes_in_use / dmap->fragment_size; + + err = + adev->d->adrv_prepare_for_output (adev->engine_num, dmap->fragment_size, + dmap->nfrags); + cuckoo_playsubstream[adev->engine_num] = substream; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return err; +} + +static int +snd_cuckoo_capture_prepare (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number, err; + adev_t *adev; + oss_native_word flags; + dmap_t *dmap; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->capture_adev[snum]; + dmap = adev->dmap_in; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + + adev->d->adrv_set_format (adev->engine_num, + snd_pcm_format_width (runtime->format)); + adev->d->adrv_set_channels (adev->engine_num, runtime->channels); + adev->d->adrv_set_rate (adev->engine_num, runtime->rate); + + dmap->bytes_in_use = snd_pcm_lib_buffer_bytes (substream); + dmap->fragment_size = snd_pcm_lib_period_bytes (substream); + +#if 1 + { + int f, s;; + + f = dmap->fragment_size / 4; + if (f < 128) + f = dmap->fragment_size / 2; + if (f < 128) + f = dmap->fragment_size; + + s = dmap->bytes_in_use; + while (s > f) + s /= 2; + + dmap->fragment_size = s; + } +#endif + + if (adev->max_block > 0 && dmap->fragment_size > adev->max_block) + dmap->fragment_size = adev->max_block; + if (adev->min_block > 0 && dmap->fragment_size < adev->min_block) + dmap->fragment_size = adev->min_block; + if (dmap->fragment_size < 8) + dmap->fragment_size = 8; + dmap->nfrags = dmap->bytes_in_use / dmap->fragment_size; + + err = + adev->d->adrv_prepare_for_input (adev->engine_num, dmap->fragment_size, + dmap->nfrags); + cuckoo_capturesubstream[adev->engine_num] = substream; + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return err; +} + +static int +snd_cuckoo_playback_trigger (snd_pcm_substream_t * substream, int cmd) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + //snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + oss_native_word flags; + dmap_t *dmap; + int err = 0; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->play_adev[snum]; + dmap = adev->dmap_out; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + + switch (cmd) + { + case SNDRV_PCM_TRIGGER_START: + adev->d->adrv_output_block (adev->engine_num, dmap->dmabuf_phys, + dmap->bytes_in_use, dmap->fragment_size, 0); + adev->d->adrv_trigger (adev->engine_num, PCM_ENABLE_OUTPUT); + break; + + case SNDRV_PCM_TRIGGER_STOP: + adev->d->adrv_trigger (adev->engine_num, 0); + break; + + default: + printk ("cuckoo: Bad trigger cmd %x\n", cmd); + err = -EIO; + goto fail; + } + +fail: + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return err; +} + +static int +snd_cuckoo_capture_trigger (snd_pcm_substream_t * substream, int cmd) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + //snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + oss_native_word flags; + dmap_t *dmap; + int err = 0; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->capture_adev[snum]; + dmap = adev->dmap_in; + + udi_spin_lock_irqsave (&adev->mutex, &flags); + + switch (cmd) + { + case SNDRV_PCM_TRIGGER_START: + adev->d->adrv_start_input (adev->engine_num, dmap->dmabuf_phys, + dmap->bytes_in_use, dmap->fragment_size, 0); + adev->d->adrv_trigger (adev->engine_num, PCM_ENABLE_INPUT); + break; + + case SNDRV_PCM_TRIGGER_STOP: + adev->d->adrv_trigger (adev->engine_num, 0); + break; + + default: + printk ("cuckoo: Bad trigger cmd %x\n", cmd); + err = -EIO; + goto fail; + } + +fail: + udi_spin_unlock_irqrestore (&adev->mutex, flags); + return err; +} + +static snd_pcm_uframes_t +snd_cuckoo_playback_pointer (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + //snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + dmap_t *dmap; + int pos; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->play_adev[snum]; + dmap = adev->dmap_out; + + if (adev->d->adrv_get_output_pointer != NULL) + pos = + adev->d->adrv_get_output_pointer (adev->engine_num, dmap, PCM_ENABLE_OUTPUT); + else + { + pos = dmap->fragment_counter * dmap->fragment_size; + } + pos = bytes_to_frames (substream->runtime, pos); + + return pos; +} + +static snd_pcm_uframes_t +snd_cuckoo_capture_pointer (snd_pcm_substream_t * substream) +{ + cuckoo_t *chip = snd_pcm_substream_chip (substream); + //snd_pcm_runtime_t *runtime = substream->runtime; + int snum = substream->number; + adev_t *adev; + dmap_t *dmap; + int pos; + + if (snum < 0 || snum >= chip->npcm) + return -ENXIO; + + adev = chip->capture_adev[snum]; + dmap = adev->dmap_in; + + if (adev->d->adrv_get_input_pointer != NULL) + pos = + adev->d->adrv_get_input_pointer (adev->engine_num, dmap, PCM_ENABLE_INPUT); + else + { + pos = dmap->fragment_counter * dmap->fragment_size; + } + pos = bytes_to_frames (substream->runtime, pos); + + return pos; +} + +static snd_pcm_ops_t snd_cuckoo_playback_ops = { + .open = snd_cuckoo_playback_open, + .close = snd_cuckoo_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cuckoo_playback_hw_params, + .hw_free = snd_cuckoo_hw_free, + .prepare = snd_cuckoo_playback_prepare, + .trigger = snd_cuckoo_playback_trigger, + .pointer = snd_cuckoo_playback_pointer, +}; + +static snd_pcm_ops_t snd_cuckoo_capture_ops = { + .open = snd_cuckoo_capture_open, + .close = snd_cuckoo_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cuckoo_capture_hw_params, + .hw_free = snd_cuckoo_hw_free, + .prepare = snd_cuckoo_capture_prepare, + .trigger = snd_cuckoo_capture_trigger, + .pointer = snd_cuckoo_capture_pointer, +}; + +int +install_pcm_instances (cuckoo_t * chip, int cardno) +{ + int dev, err, ok = 0; + int ninputs = 0, noutputs = 0; + + for (dev = 0; dev < num_audio_devfiles; dev++) + if (audio_devfiles[dev]->card_number == cardno) + { + adev_t *adev = audio_devfiles[dev]; + adev_t *nextdev = audio_devfiles[dev + 1]; + snd_pcm_t *pcm; + + ninputs = noutputs = 0; + + ok = 0; +/* Special handling for shadow devices */ + if (dev < num_audio_devfiles - 1 && (adev->flags & ADEV_DUPLEX)) + if ((nextdev->flags & ADEV_DUPLEX) + && (nextdev->flags & ADEV_SHADOW)) + ok = 1; + +// Devices with one recording engine and multiple playback ones + if (dev < num_audio_devfiles - 1 && (adev->flags & ADEV_DUPLEX)) + if (adev->card_number == nextdev->card_number) + if ((nextdev->flags & ADEV_NOINPUT)) + ok = 1; + + if (ok) // Device needs special handling + { + if ((err = + snd_pcm_new (chip->card, "OSS/Linux", chip->npcm, 1, 1, + &pcm)) < 0) + { + printk ("cuckoo: snd_pcm_new failed - error %d\n", err); + return err; + } + + pcm->private_data = chip; + chip->pcm[chip->npcm++] = pcm; + strlcpy (pcm->name, adev->name); + + chip->capture_adev[chip->ncapture++] = nextdev; + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_cuckoo_capture_ops); + + chip->play_adev[chip->nplay++] = adev; + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_cuckoo_playback_ops); + + dev++; + continue; + } + + if (!(adev->flags & ADEV_NOINPUT)) + ninputs = 1; + if (!(adev->flags & ADEV_NOOUTPUT)) + noutputs = 1; + + if ((err = + snd_pcm_new (chip->card, "OSS/Linux", chip->npcm, noutputs, + ninputs, &pcm)) < 0) + { + printk ("cuckoo: snd_pcm_new failed - error %d\n", err); + return err; + } + + pcm->private_data = chip; + chip->pcm[chip->npcm++] = pcm; + strlcpy (pcm->name, adev->name); + + if (noutputs > 0) + { + chip->play_adev[chip->nplay++] = adev; + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_cuckoo_playback_ops); + } + + if (ninputs > 0) + { + chip->capture_adev[chip->ncapture++] = adev; + snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_cuckoo_capture_ops); + } + } + + return 0; +} + +EXPORT_SYMBOL (install_pcm_instances); |