Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
89
sound/Kconfig
Normal file
89
sound/Kconfig
Normal file
@@ -0,0 +1,89 @@
|
||||
# sound/Config.in
|
||||
#
|
||||
|
||||
menu "Sound"
|
||||
|
||||
config SOUND
|
||||
tristate "Sound card support"
|
||||
help
|
||||
If you have a sound card in your computer, i.e. if it can say more
|
||||
than an occasional beep, say Y. Be sure to have all the information
|
||||
about your sound card and its configuration down (I/O port,
|
||||
interrupt and DMA channel), because you will be asked for it.
|
||||
|
||||
You want to read the Sound-HOWTO, available from
|
||||
<http://www.tldp.org/docs.html#howto>. General information about
|
||||
the modular sound system is contained in the files
|
||||
<file:Documentation/sound/oss/Introduction>. The file
|
||||
<file:Documentation/sound/oss/README.OSS> contains some slightly
|
||||
outdated but still useful information as well. Newer sound
|
||||
driver documentation is found in <file:Documentation/sound/alsa/*>.
|
||||
|
||||
If you have a PnP sound card and you want to configure it at boot
|
||||
time using the ISA PnP tools (read
|
||||
<http://www.roestock.demon.co.uk/isapnptools/>), then you need to
|
||||
compile the sound card support as a module and load that module
|
||||
after the PnP configuration is finished. To do this, choose M here
|
||||
and read <file:Documentation/sound/oss/README.modules>; the module
|
||||
will be called soundcore.
|
||||
|
||||
I'm told that even without a sound card, you can make your computer
|
||||
say more than an occasional beep, by programming the PC speaker.
|
||||
Kernel patches and supporting utilities to do that are in the pcsp
|
||||
package, available at <ftp://ftp.infradead.org/pub/pcsp/>.
|
||||
|
||||
source "sound/oss/dmasound/Kconfig"
|
||||
|
||||
if !M68K
|
||||
|
||||
menu "Advanced Linux Sound Architecture"
|
||||
depends on SOUND!=n
|
||||
|
||||
config SND
|
||||
tristate "Advanced Linux Sound Architecture"
|
||||
depends on SOUND
|
||||
|
||||
source "sound/core/Kconfig"
|
||||
|
||||
source "sound/drivers/Kconfig"
|
||||
|
||||
source "sound/isa/Kconfig"
|
||||
|
||||
source "sound/pci/Kconfig"
|
||||
|
||||
source "sound/ppc/Kconfig"
|
||||
|
||||
source "sound/arm/Kconfig"
|
||||
|
||||
source "sound/mips/Kconfig"
|
||||
|
||||
# the following will depenend on the order of config.
|
||||
# here assuming USB is defined before ALSA
|
||||
source "sound/usb/Kconfig"
|
||||
|
||||
# the following will depenend on the order of config.
|
||||
# here assuming PCMCIA is defined before ALSA
|
||||
source "sound/pcmcia/Kconfig"
|
||||
|
||||
source "sound/sparc/Kconfig"
|
||||
|
||||
source "sound/parisc/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Open Sound System"
|
||||
depends on SOUND!=n && (BROKEN || (!SPARC32 && !SPARC64))
|
||||
|
||||
config SOUND_PRIME
|
||||
tristate "Open Sound System (DEPRECATED)"
|
||||
depends on SOUND
|
||||
help
|
||||
Say 'Y' or 'M' to enable Open Sound System drivers.
|
||||
|
||||
source "sound/oss/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
13
sound/Makefile
Normal file
13
sound/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Makefile for the Linux sound card driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SOUND) += soundcore.o
|
||||
obj-$(CONFIG_SOUND_PRIME) += oss/
|
||||
obj-$(CONFIG_DMASOUND) += oss/
|
||||
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
|
||||
|
||||
ifeq ($(CONFIG_SND),y)
|
||||
obj-y += last.o
|
||||
endif
|
||||
|
||||
soundcore-objs := sound_core.o sound_firmware.o
|
||||
18
sound/arm/Kconfig
Normal file
18
sound/arm/Kconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
# ALSA ARM drivers
|
||||
|
||||
menu "ALSA ARM devices"
|
||||
depends on SND!=n && ARM
|
||||
|
||||
config SND_SA11XX_UDA1341
|
||||
tristate "SA11xx UDA1341TS driver (iPaq H3600)"
|
||||
depends on ARCH_SA1100 && SND && L3
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y here if you have a Compaq iPaq H3x00 handheld computer
|
||||
and want to use its Philips UDA 1341 audio chip.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-sa11xx-uda1341.
|
||||
|
||||
endmenu
|
||||
|
||||
8
sound/arm/Makefile
Normal file
8
sound/arm/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
#
|
||||
|
||||
snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o
|
||||
973
sound/arm/sa11xx-uda1341.c
Normal file
973
sound/arm/sa11xx-uda1341.c
Normal file
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
* Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard
|
||||
* Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License.
|
||||
*
|
||||
* History:
|
||||
*
|
||||
* 2002-03-13 Tomas Kasparek initial release - based on h3600-uda1341.c from OSS
|
||||
* 2002-03-20 Tomas Kasparek playback over ALSA is working
|
||||
* 2002-03-28 Tomas Kasparek playback over OSS emulation is working
|
||||
* 2002-03-29 Tomas Kasparek basic capture is working (native ALSA)
|
||||
* 2002-03-29 Tomas Kasparek capture is working (OSS emulation)
|
||||
* 2002-04-04 Tomas Kasparek better rates handling (allow non-standard rates)
|
||||
* 2003-02-14 Brian Avery fixed full duplex mode, other updates
|
||||
* 2003-02-20 Tomas Kasparek merged updates by Brian (except HAL)
|
||||
* 2003-04-19 Jaroslav Kysela recoded DMA stuff to follow 2.4.18rmk3-hh24 kernel
|
||||
* working suspend and resume
|
||||
* 2003-04-28 Tomas Kasparek updated work by Jaroslav to compile it under 2.5.x again
|
||||
* merged HAL layer (patches from Brian)
|
||||
*/
|
||||
|
||||
/* $Id: sa11xx-uda1341.c,v 1.21 2005/01/28 19:34:04 tiwai Exp $ */
|
||||
|
||||
/***************************************************************************************************
|
||||
*
|
||||
* To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai
|
||||
* available in the Alsa doc section on the website
|
||||
*
|
||||
* A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.
|
||||
* We are using SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated
|
||||
* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.
|
||||
* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the
|
||||
* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which
|
||||
* is a mem loc that always decodes to 0's w/ no off chip access.
|
||||
*
|
||||
* Some alsa terminology:
|
||||
* frame => num_channels * sample_size e.g stereo 16 bit is 2 * 16 = 32 bytes
|
||||
* period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte
|
||||
* buffer and 4 periods in the runtime structure this means we'll get an int every 256
|
||||
* bytes or 4 times per buffer.
|
||||
* A number of the sizes are in frames rather than bytes, use frames_to_bytes and
|
||||
* bytes_to_frames to convert. The easiest way to tell the units is to look at the
|
||||
* type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t
|
||||
*
|
||||
* Notes about the pointer fxn:
|
||||
* The pointer fxn needs to return the offset into the dma buffer in frames.
|
||||
* Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.
|
||||
*
|
||||
* Notes about pause/resume
|
||||
* Implementing this would be complicated so it's skipped. The problem case is:
|
||||
* A full duplex connection is going, then play is paused. At this point you need to start xmitting
|
||||
* 0's to keep the record active which means you cant just freeze the dma and resume it later you'd
|
||||
* need to save off the dma info, and restore it properly on a resume. Yeach!
|
||||
*
|
||||
* Notes about transfer methods:
|
||||
* The async write calls fail. I probably need to implement something else to support them?
|
||||
*
|
||||
***************************************************************************************************/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <sound/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#include <linux/pm.h>
|
||||
#endif
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/h3600.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/arch/h3600_hal.h>
|
||||
#endif
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include <linux/l3/l3.h>
|
||||
|
||||
#undef DEBUG_MODE
|
||||
#undef DEBUG_FUNCTION_NAMES
|
||||
#include <sound/uda1341.h>
|
||||
|
||||
/*
|
||||
* FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels?
|
||||
* We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this
|
||||
* module for Familiar 0.6.1
|
||||
*/
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
#define HH_VERSION 1
|
||||
#endif
|
||||
|
||||
/* {{{ Type definitions */
|
||||
|
||||
MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
|
||||
MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}");
|
||||
|
||||
static char *id = NULL; /* ID for this card */
|
||||
|
||||
module_param(id, charp, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
|
||||
|
||||
typedef struct audio_stream {
|
||||
char *id; /* identification string */
|
||||
int stream_id; /* numeric identification */
|
||||
dma_device_t dma_dev; /* device identifier for DMA */
|
||||
#ifdef HH_VERSION
|
||||
dmach_t dmach; /* dma channel identification */
|
||||
#else
|
||||
dma_regs_t *dma_regs; /* points to our DMA registers */
|
||||
#endif
|
||||
int active:1; /* we are using this stream for transfer now */
|
||||
int period; /* current transfer period */
|
||||
int periods; /* current count of periods registerd in the DMA engine */
|
||||
int tx_spin; /* are we recoding - flag used to do DMA trans. for sync */
|
||||
unsigned int old_offset;
|
||||
spinlock_t dma_lock; /* for locking in DMA operations (see dma-sa1100.c in the kernel) */
|
||||
snd_pcm_substream_t *stream;
|
||||
}audio_stream_t;
|
||||
|
||||
typedef struct snd_card_sa11xx_uda1341 {
|
||||
snd_card_t *card;
|
||||
struct l3_client *uda1341;
|
||||
snd_pcm_t *pcm;
|
||||
long samplerate;
|
||||
audio_stream_t s[2]; /* playback & capture */
|
||||
} sa11xx_uda1341_t;
|
||||
|
||||
static struct snd_card_sa11xx_uda1341 *sa11xx_uda1341 = NULL;
|
||||
|
||||
static unsigned int rates[] = {
|
||||
8000, 10666, 10985, 14647,
|
||||
16000, 21970, 22050, 24000,
|
||||
29400, 32000, 44100, 48000,
|
||||
};
|
||||
|
||||
static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ Clock and sample rate stuff */
|
||||
|
||||
/*
|
||||
* Stop-gap solution until rest of hh.org HAL stuff is merged.
|
||||
*/
|
||||
#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12)
|
||||
#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13)
|
||||
|
||||
#ifdef CONFIG_SA1100_H3XXX
|
||||
#define clr_sa11xx_uda1341_egpio(x) clr_h3600_egpio(x)
|
||||
#define set_sa11xx_uda1341_egpio(x) set_h3600_egpio(x)
|
||||
#else
|
||||
#error This driver could serve H3x00 handhelds only!
|
||||
#endif
|
||||
|
||||
static void sa11xx_uda1341_set_audio_clock(long val)
|
||||
{
|
||||
switch (val) {
|
||||
case 24000: case 32000: case 48000: /* 00: 12.288 MHz */
|
||||
GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
|
||||
break;
|
||||
|
||||
case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */
|
||||
GPSR = GPIO_H3600_CLK_SET0;
|
||||
GPCR = GPIO_H3600_CLK_SET1;
|
||||
break;
|
||||
|
||||
case 8000: case 10666: case 16000: /* 10: 4.096 MHz */
|
||||
GPCR = GPIO_H3600_CLK_SET0;
|
||||
GPSR = GPIO_H3600_CLK_SET1;
|
||||
break;
|
||||
|
||||
case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */
|
||||
GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sa11xx_uda1341_set_samplerate(sa11xx_uda1341_t *sa11xx_uda1341, long rate)
|
||||
{
|
||||
int clk_div = 0;
|
||||
int clk=0;
|
||||
|
||||
/* We don't want to mess with clocks when frames are in flight */
|
||||
Ser4SSCR0 &= ~SSCR0_SSE;
|
||||
/* wait for any frame to complete */
|
||||
udelay(125);
|
||||
|
||||
/*
|
||||
* We have the following clock sources:
|
||||
* 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
|
||||
* Those can be divided either by 256, 384 or 512.
|
||||
* This makes up 12 combinations for the following samplerates...
|
||||
*/
|
||||
if (rate >= 48000)
|
||||
rate = 48000;
|
||||
else if (rate >= 44100)
|
||||
rate = 44100;
|
||||
else if (rate >= 32000)
|
||||
rate = 32000;
|
||||
else if (rate >= 29400)
|
||||
rate = 29400;
|
||||
else if (rate >= 24000)
|
||||
rate = 24000;
|
||||
else if (rate >= 22050)
|
||||
rate = 22050;
|
||||
else if (rate >= 21970)
|
||||
rate = 21970;
|
||||
else if (rate >= 16000)
|
||||
rate = 16000;
|
||||
else if (rate >= 14647)
|
||||
rate = 14647;
|
||||
else if (rate >= 10985)
|
||||
rate = 10985;
|
||||
else if (rate >= 10666)
|
||||
rate = 10666;
|
||||
else
|
||||
rate = 8000;
|
||||
|
||||
/* Set the external clock generator */
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
h3600_audio_clock(rate);
|
||||
#else
|
||||
sa11xx_uda1341_set_audio_clock(rate);
|
||||
#endif
|
||||
|
||||
/* Select the clock divisor */
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
case 10985:
|
||||
case 22050:
|
||||
case 24000:
|
||||
clk = F512;
|
||||
clk_div = SSCR0_SerClkDiv(16);
|
||||
break;
|
||||
case 16000:
|
||||
case 21970:
|
||||
case 44100:
|
||||
case 48000:
|
||||
clk = F256;
|
||||
clk_div = SSCR0_SerClkDiv(8);
|
||||
break;
|
||||
case 10666:
|
||||
case 14647:
|
||||
case 29400:
|
||||
case 32000:
|
||||
clk = F384;
|
||||
clk_div = SSCR0_SerClkDiv(12);
|
||||
break;
|
||||
}
|
||||
|
||||
/* FMT setting should be moved away when other FMTs are added (FIXME) */
|
||||
l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16);
|
||||
|
||||
l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);
|
||||
Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
|
||||
sa11xx_uda1341->samplerate = rate;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ HW init and shutdown */
|
||||
|
||||
static void sa11xx_uda1341_audio_init(sa11xx_uda1341_t *sa11xx_uda1341)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* Setup DMA stuff */
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].id = "UDA1341 out";
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = DMA_Ser4SSPWr;
|
||||
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].id = "UDA1341 in";
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;
|
||||
sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = DMA_Ser4SSPRd;
|
||||
|
||||
/* Initialize the UDA1341 internal state */
|
||||
|
||||
/* Setup the uarts */
|
||||
local_irq_save(flags);
|
||||
GAFR |= (GPIO_SSP_CLK);
|
||||
GPDR &= ~(GPIO_SSP_CLK);
|
||||
Ser4SSCR0 = 0;
|
||||
Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
|
||||
Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
|
||||
Ser4SSCR0 |= SSCR0_SSE;
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* Enable the audio power */
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
h3600_audio_power(AUDIO_RATE_DEFAULT);
|
||||
#else
|
||||
clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
|
||||
set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
|
||||
set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
|
||||
#endif
|
||||
|
||||
/* Wait for the UDA1341 to wake up */
|
||||
mdelay(1); //FIXME - was removed by Perex - Why?
|
||||
|
||||
/* Initialize the UDA1341 internal state */
|
||||
l3_open(sa11xx_uda1341->uda1341);
|
||||
|
||||
/* external clock configuration (after l3_open - regs must be initialized */
|
||||
sa11xx_uda1341_set_samplerate(sa11xx_uda1341, sa11xx_uda1341->samplerate);
|
||||
|
||||
/* Wait for the UDA1341 to wake up */
|
||||
set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
|
||||
mdelay(1);
|
||||
|
||||
/* make the left and right channels unswapped (flip the WS latch) */
|
||||
Ser4SSDR = 0;
|
||||
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
h3600_audio_mute(0);
|
||||
#else
|
||||
clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void sa11xx_uda1341_audio_shutdown(sa11xx_uda1341_t *sa11xx_uda1341)
|
||||
{
|
||||
/* mute on */
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
h3600_audio_mute(1);
|
||||
#else
|
||||
set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
|
||||
#endif
|
||||
|
||||
/* disable the audio power and all signals leading to the audio chip */
|
||||
l3_close(sa11xx_uda1341->uda1341);
|
||||
Ser4SSCR0 = 0;
|
||||
clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
|
||||
|
||||
/* power off and mute off */
|
||||
/* FIXME - is muting off necesary??? */
|
||||
#ifdef CONFIG_H3600_HAL
|
||||
h3600_audio_power(0);
|
||||
h3600_audio_mute(0);
|
||||
#else
|
||||
clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
|
||||
clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ DMA staff */
|
||||
|
||||
/*
|
||||
* these are the address and sizes used to fill the xmit buffer
|
||||
* so we can get a clock in record only mode
|
||||
*/
|
||||
#define FORCE_CLOCK_ADDR (dma_addr_t)FLUSH_BASE_PHYS
|
||||
#define FORCE_CLOCK_SIZE 4096 // was 2048
|
||||
|
||||
// FIXME Why this value exactly - wrote comment
|
||||
#define DMA_BUF_SIZE 8176 /* <= MAX_DMA_SIZE from asm/arch-sa1100/dma.h */
|
||||
|
||||
#ifdef HH_VERSION
|
||||
|
||||
static int audio_dma_request(audio_stream_t *s, void (*callback)(void *, int))
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sa1100_request_dma(&s->dmach, s->id, s->dma_dev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
|
||||
return ret;
|
||||
}
|
||||
sa1100_dma_set_callback(s->dmach, callback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void audio_dma_free(audio_stream_t *s)
|
||||
{
|
||||
sa1100_free_dma(s->dmach);
|
||||
s->dmach = -1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int audio_dma_request(audio_stream_t *s, void (*callback)(void *))
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sa1100_request_dma(s->dma_dev, s->id, callback, s, &s->dma_regs);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "unable to grab audio dma 0x%x\n", s->dma_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void audio_dma_free(audio_stream_t *s)
|
||||
{
|
||||
sa1100_free_dma((s)->dma_regs);
|
||||
(s)->dma_regs = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static u_int audio_get_dma_pos(audio_stream_t *s)
|
||||
{
|
||||
snd_pcm_substream_t * substream = s->stream;
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
unsigned int offset;
|
||||
unsigned long flags;
|
||||
dma_addr_t addr;
|
||||
|
||||
// this must be called w/ interrupts locked out see dma-sa1100.c in the kernel
|
||||
spin_lock_irqsave(&s->dma_lock, flags);
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_get_current(s->dmach, NULL, &addr);
|
||||
#else
|
||||
addr = sa1100_get_dma_pos((s)->dma_regs);
|
||||
#endif
|
||||
offset = addr - runtime->dma_addr;
|
||||
spin_unlock_irqrestore(&s->dma_lock, flags);
|
||||
|
||||
offset = bytes_to_frames(runtime,offset);
|
||||
if (offset >= runtime->buffer_size)
|
||||
offset = 0;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* this stops the dma and clears the dma ptrs
|
||||
*/
|
||||
static void audio_stop_dma(audio_stream_t *s)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&s->dma_lock, flags);
|
||||
s->active = 0;
|
||||
s->period = 0;
|
||||
/* this stops the dma channel and clears the buffer ptrs */
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_flush_all(s->dmach);
|
||||
#else
|
||||
sa1100_clear_dma(s->dma_regs);
|
||||
#endif
|
||||
spin_unlock_irqrestore(&s->dma_lock, flags);
|
||||
}
|
||||
|
||||
static void audio_process_dma(audio_stream_t *s)
|
||||
{
|
||||
snd_pcm_substream_t *substream = s->stream;
|
||||
snd_pcm_runtime_t *runtime;
|
||||
unsigned int dma_size;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
/* we are requested to process synchronization DMA transfer */
|
||||
if (s->tx_spin) {
|
||||
snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return);
|
||||
/* fill the xmit dma buffers and return */
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
|
||||
#else
|
||||
while (1) {
|
||||
ret = sa1100_start_dma(s->dma_regs, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* must be set here - only valid for running streams, not for forced_clock dma fills */
|
||||
runtime = substream->runtime;
|
||||
while (s->active && s->periods < runtime->periods) {
|
||||
dma_size = frames_to_bytes(runtime, runtime->period_size);
|
||||
if (s->old_offset) {
|
||||
/* a little trick, we need resume from old position */
|
||||
offset = frames_to_bytes(runtime, s->old_offset - 1);
|
||||
s->old_offset = 0;
|
||||
s->periods = 0;
|
||||
s->period = offset / dma_size;
|
||||
offset %= dma_size;
|
||||
dma_size = dma_size - offset;
|
||||
if (!dma_size)
|
||||
continue; /* special case */
|
||||
} else {
|
||||
offset = dma_size * s->period;
|
||||
snd_assert(dma_size <= DMA_BUF_SIZE, );
|
||||
}
|
||||
#ifdef HH_VERSION
|
||||
ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size);
|
||||
if (ret)
|
||||
return; //FIXME
|
||||
#else
|
||||
ret = sa1100_start_dma((s)->dma_regs, runtime->dma_addr + offset, dma_size);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
s->period++;
|
||||
s->period %= runtime->periods;
|
||||
s->periods++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HH_VERSION
|
||||
static void audio_dma_callback(void *data, int size)
|
||||
#else
|
||||
static void audio_dma_callback(void *data)
|
||||
#endif
|
||||
{
|
||||
audio_stream_t *s = data;
|
||||
|
||||
/*
|
||||
* If we are getting a callback for an active stream then we inform
|
||||
* the PCM middle layer we've finished a period
|
||||
*/
|
||||
if (s->active)
|
||||
snd_pcm_period_elapsed(s->stream);
|
||||
|
||||
spin_lock(&s->dma_lock);
|
||||
if (!s->tx_spin && s->periods > 0)
|
||||
s->periods--;
|
||||
audio_process_dma(s);
|
||||
spin_unlock(&s->dma_lock);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PCM setting */
|
||||
|
||||
/* {{{ trigger & timer */
|
||||
|
||||
static int snd_sa11xx_uda1341_trigger(snd_pcm_substream_t * substream, int cmd)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
|
||||
int stream_id = substream->pstr->stream;
|
||||
audio_stream_t *s = &chip->s[stream_id];
|
||||
audio_stream_t *s1 = &chip->s[stream_id ^ 1];
|
||||
int err = 0;
|
||||
|
||||
/* note local interrupts are already disabled in the midlevel code */
|
||||
spin_lock(&s->dma_lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
/* now we need to make sure a record only stream has a clock */
|
||||
if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
|
||||
/* we need to force fill the xmit DMA with zeros */
|
||||
s1->tx_spin = 1;
|
||||
audio_process_dma(s1);
|
||||
}
|
||||
/* this case is when you were recording then you turn on a
|
||||
* playback stream so we stop (also clears it) the dma first,
|
||||
* clear the sync flag and then we let it turned on
|
||||
*/
|
||||
else {
|
||||
s->tx_spin = 0;
|
||||
}
|
||||
|
||||
/* requested stream startup */
|
||||
s->active = 1;
|
||||
audio_process_dma(s);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
/* requested stream shutdown */
|
||||
audio_stop_dma(s);
|
||||
|
||||
/*
|
||||
* now we need to make sure a record only stream has a clock
|
||||
* so if we're stopping a playback with an active capture
|
||||
* we need to turn the 0 fill dma on for the xmit side
|
||||
*/
|
||||
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) {
|
||||
/* we need to force fill the xmit DMA with zeros */
|
||||
s->tx_spin = 1;
|
||||
audio_process_dma(s);
|
||||
}
|
||||
/*
|
||||
* we killed a capture only stream, so we should also kill
|
||||
* the zero fill transmit
|
||||
*/
|
||||
else {
|
||||
if (s1->tx_spin) {
|
||||
s1->tx_spin = 0;
|
||||
audio_stop_dma(s1);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
s->active = 0;
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_stop(s->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
s->old_offset = audio_get_dma_pos(s) + 1;
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_flush_all(s->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
s->periods = 0;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
s->active = 1;
|
||||
s->tx_spin = 0;
|
||||
audio_process_dma(s);
|
||||
if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
|
||||
s1->tx_spin = 1;
|
||||
audio_process_dma(s1);
|
||||
}
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_stop(s->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
s->active = 0;
|
||||
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (s1->active) {
|
||||
s->tx_spin = 1;
|
||||
s->old_offset = audio_get_dma_pos(s) + 1;
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_flush_all(s->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
audio_process_dma(s);
|
||||
}
|
||||
} else {
|
||||
if (s1->tx_spin) {
|
||||
s1->tx_spin = 0;
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_flush_all(s1->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
s->active = 1;
|
||||
if (s->old_offset) {
|
||||
s->tx_spin = 0;
|
||||
audio_process_dma(s);
|
||||
break;
|
||||
}
|
||||
if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) {
|
||||
s1->tx_spin = 1;
|
||||
audio_process_dma(s1);
|
||||
}
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_resume(s->dmach);
|
||||
#else
|
||||
//FIXME - DMA API
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&s->dma_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_sa11xx_uda1341_prepare(snd_pcm_substream_t * substream)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
audio_stream_t *s = &chip->s[substream->pstr->stream];
|
||||
|
||||
/* set requested samplerate */
|
||||
sa11xx_uda1341_set_samplerate(chip, runtime->rate);
|
||||
|
||||
/* set requestd format when available */
|
||||
/* set FMT here !!! FIXME */
|
||||
|
||||
s->period = 0;
|
||||
s->periods = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_sa11xx_uda1341_pointer(snd_pcm_substream_t * substream)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
|
||||
return audio_get_dma_pos(&chip->s[substream->pstr->stream]);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
static snd_pcm_hardware_t snd_sa11xx_uda1341_capture =
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 64*1024,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = DMA_BUF_SIZE,
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static snd_pcm_hardware_t snd_sa11xx_uda1341_playback =
|
||||
{
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
|
||||
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
|
||||
SNDRV_PCM_RATE_KNOT),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 64*1024,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = DMA_BUF_SIZE,
|
||||
.periods_min = 2,
|
||||
.periods_max = 255,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int snd_card_sa11xx_uda1341_open(snd_pcm_substream_t * substream)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
int stream_id = substream->pstr->stream;
|
||||
int err;
|
||||
|
||||
chip->s[stream_id].stream = substream;
|
||||
|
||||
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
runtime->hw = snd_sa11xx_uda1341_playback;
|
||||
else
|
||||
runtime->hw = snd_sa11xx_uda1341_capture;
|
||||
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
|
||||
return err;
|
||||
if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_sa11xx_uda1341_close(snd_pcm_substream_t * substream)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
chip->s[substream->pstr->stream].stream = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* {{{ HW params & free */
|
||||
|
||||
static int snd_sa11xx_uda1341_hw_params(snd_pcm_substream_t * substream,
|
||||
snd_pcm_hw_params_t * hw_params)
|
||||
{
|
||||
|
||||
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
}
|
||||
|
||||
static int snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = {
|
||||
.open = snd_card_sa11xx_uda1341_open,
|
||||
.close = snd_card_sa11xx_uda1341_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_sa11xx_uda1341_hw_params,
|
||||
.hw_free = snd_sa11xx_uda1341_hw_free,
|
||||
.prepare = snd_sa11xx_uda1341_prepare,
|
||||
.trigger = snd_sa11xx_uda1341_trigger,
|
||||
.pointer = snd_sa11xx_uda1341_pointer,
|
||||
};
|
||||
|
||||
static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = {
|
||||
.open = snd_card_sa11xx_uda1341_open,
|
||||
.close = snd_card_sa11xx_uda1341_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_sa11xx_uda1341_hw_params,
|
||||
.hw_free = snd_sa11xx_uda1341_hw_free,
|
||||
.prepare = snd_sa11xx_uda1341_prepare,
|
||||
.trigger = snd_sa11xx_uda1341_trigger,
|
||||
.pointer = snd_sa11xx_uda1341_pointer,
|
||||
};
|
||||
|
||||
static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device)
|
||||
{
|
||||
snd_pcm_t *pcm;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* this sets up our initial buffers and sets the dma_type to isa.
|
||||
* isa works but I'm not sure why (or if) it's the right choice
|
||||
* this may be too large, trying it for now
|
||||
*/
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_ISA,
|
||||
snd_pcm_dma_flags(0),
|
||||
64*1024, 64*1024);
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops);
|
||||
pcm->private_data = sa11xx_uda1341;
|
||||
pcm->info_flags = 0;
|
||||
strcpy(pcm->name, "UDA1341 PCM");
|
||||
|
||||
sa11xx_uda1341_audio_init(sa11xx_uda1341);
|
||||
|
||||
/* setup DMA controller */
|
||||
audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback);
|
||||
audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback);
|
||||
|
||||
sa11xx_uda1341->pcm = pcm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ module init & exit */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int snd_sa11xx_uda1341_suspend(snd_card_t *card, pm_message_t state)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = card->pm_private_data;
|
||||
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
|
||||
sa1100_dma_sleep(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
|
||||
#else
|
||||
//FIXME
|
||||
#endif
|
||||
l3_command(chip->uda1341, CMD_SUSPEND, NULL);
|
||||
sa11xx_uda1341_audio_shutdown(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_sa11xx_uda1341_resume(snd_card_t *card)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = card->pm_private_data;
|
||||
|
||||
sa11xx_uda1341_audio_init(chip);
|
||||
l3_command(chip->uda1341, CMD_RESUME, NULL);
|
||||
#ifdef HH_VERSION
|
||||
sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dmach);
|
||||
sa1100_dma_wakeup(chip->s[SNDRV_PCM_STREAM_CAPTURE].dmach);
|
||||
#else
|
||||
//FIXME
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif /* COMFIG_PM */
|
||||
|
||||
void snd_sa11xx_uda1341_free(snd_card_t *card)
|
||||
{
|
||||
sa11xx_uda1341_t *chip = card->private_data;
|
||||
|
||||
audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
|
||||
audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
sa11xx_uda1341 = NULL;
|
||||
card->private_data = NULL;
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static int __init sa11xx_uda1341_init(void)
|
||||
{
|
||||
int err;
|
||||
snd_card_t *card;
|
||||
|
||||
if (!machine_is_h3xxx())
|
||||
return -ENODEV;
|
||||
|
||||
/* register the soundcard */
|
||||
card = snd_card_new(-1, id, THIS_MODULE, sizeof(sa11xx_uda1341_t));
|
||||
if (card == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
sa11xx_uda1341 = kcalloc(1, sizeof(*sa11xx_uda1341), GFP_KERNEL);
|
||||
if (sa11xx_uda1341 == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock_init(&chip->s[0].dma_lock);
|
||||
spin_lock_init(&chip->s[1].dma_lock);
|
||||
|
||||
card->private_data = (void *)sa11xx_uda1341;
|
||||
card->private_free = snd_sa11xx_uda1341_free;
|
||||
|
||||
sa11xx_uda1341->card = card;
|
||||
sa11xx_uda1341->samplerate = AUDIO_RATE_DEFAULT;
|
||||
|
||||
// mixer
|
||||
if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341)))
|
||||
goto nodev;
|
||||
|
||||
// PCM
|
||||
if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0)) < 0)
|
||||
goto nodev;
|
||||
|
||||
snd_card_set_generic_pm_callback(card,
|
||||
snd_sa11xx_uda1341_suspend, snd_sa11_uda1341_resume,
|
||||
sa11xx_uda1341);
|
||||
|
||||
strcpy(card->driver, "UDA1341");
|
||||
strcpy(card->shortname, "H3600 UDA1341TS");
|
||||
sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS");
|
||||
|
||||
if ((err = snd_card_register(card)) == 0) {
|
||||
printk( KERN_INFO "iPAQ audio support initialized\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
nodev:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit sa11xx_uda1341_exit(void)
|
||||
{
|
||||
snd_card_free(sa11xx_uda1341->card);
|
||||
}
|
||||
|
||||
module_init(sa11xx_uda1341_init);
|
||||
module_exit(sa11xx_uda1341_exit);
|
||||
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
133
sound/core/Kconfig
Normal file
133
sound/core/Kconfig
Normal file
@@ -0,0 +1,133 @@
|
||||
# ALSA soundcard-configuration
|
||||
config SND_TIMER
|
||||
tristate
|
||||
depends on SND
|
||||
|
||||
config SND_PCM
|
||||
tristate
|
||||
select SND_TIMER
|
||||
depends on SND
|
||||
|
||||
config SND_HWDEP
|
||||
tristate
|
||||
depends on SND
|
||||
|
||||
config SND_RAWMIDI
|
||||
tristate
|
||||
depends on SND
|
||||
|
||||
config SND_SEQUENCER
|
||||
tristate "Sequencer support"
|
||||
depends on SND
|
||||
select SND_TIMER
|
||||
help
|
||||
Say Y or M to enable MIDI sequencer and router support. This
|
||||
feature allows routing and enqueueing of MIDI events. Events
|
||||
can be processed at a given time.
|
||||
|
||||
Many programs require this feature, so you should enable it
|
||||
unless you know what you're doing.
|
||||
|
||||
config SND_SEQ_DUMMY
|
||||
tristate "Sequencer dummy client"
|
||||
depends on SND_SEQUENCER
|
||||
help
|
||||
Say Y here to enable the dummy sequencer client. This client
|
||||
is a simple MIDI-through client: all normal input events are
|
||||
redirected to the output port immediately.
|
||||
|
||||
You don't need this unless you want to connect many MIDI
|
||||
devices or applications together.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-seq-dummy.
|
||||
|
||||
config SND_OSSEMUL
|
||||
bool
|
||||
depends on SND
|
||||
|
||||
config SND_MIXER_OSS
|
||||
tristate "OSS Mixer API"
|
||||
depends on SND
|
||||
select SND_OSSEMUL
|
||||
help
|
||||
To enable OSS mixer API emulation (/dev/mixer*), say Y here
|
||||
and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
|
||||
|
||||
Many programs still use the OSS API, so say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-mixer-oss.
|
||||
|
||||
config SND_PCM_OSS
|
||||
tristate "OSS PCM (digital audio) API"
|
||||
depends on SND
|
||||
select SND_OSSEMUL
|
||||
select SND_PCM
|
||||
help
|
||||
To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y
|
||||
here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
|
||||
|
||||
Many programs still use the OSS API, so say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-pcm-oss.
|
||||
|
||||
config SND_SEQUENCER_OSS
|
||||
bool "OSS Sequencer API"
|
||||
depends on SND && SND_SEQUENCER
|
||||
select SND_OSSEMUL
|
||||
help
|
||||
Say Y here to enable OSS sequencer emulation (both
|
||||
/dev/sequencer and /dev/music interfaces).
|
||||
|
||||
Many programs still use the OSS API, so say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-seq-oss.
|
||||
|
||||
config SND_RTCTIMER
|
||||
tristate "RTC Timer support"
|
||||
depends on SND && RTC
|
||||
select SND_TIMER
|
||||
help
|
||||
Say Y here to enable RTC timer support for ALSA. ALSA uses
|
||||
the RTC timer as a precise timing source and maps the RTC
|
||||
timer to ALSA's timer interface. The ALSA sequencer code also
|
||||
can use this timing source.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-rtctimer.
|
||||
|
||||
config SND_VERBOSE_PRINTK
|
||||
bool "Verbose printk"
|
||||
depends on SND
|
||||
help
|
||||
Say Y here to enable verbose log messages. These messages
|
||||
will help to identify source file and position containing
|
||||
printed messages.
|
||||
|
||||
You don't need this unless you're debugging ALSA.
|
||||
|
||||
config SND_DEBUG
|
||||
bool "Debug"
|
||||
depends on SND
|
||||
help
|
||||
Say Y here to enable ALSA debug code.
|
||||
|
||||
config SND_DEBUG_MEMORY
|
||||
bool "Debug memory"
|
||||
depends on SND_DEBUG
|
||||
help
|
||||
Say Y here to enable debugging of memory allocations.
|
||||
|
||||
config SND_DEBUG_DETECT
|
||||
bool "Debug detection"
|
||||
depends on SND_DEBUG
|
||||
help
|
||||
Say Y here to enable extra-verbose log messages printed when
|
||||
detecting devices.
|
||||
|
||||
config SND_GENERIC_PM
|
||||
bool
|
||||
depends on SND
|
||||
33
sound/core/Makefile
Normal file
33
sound/core/Makefile
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
|
||||
#
|
||||
|
||||
snd-objs := sound.o init.o memory.o info.o control.o misc.o \
|
||||
device.o wrappers.o
|
||||
ifeq ($(CONFIG_ISA),y)
|
||||
snd-objs += isadma.o
|
||||
endif
|
||||
ifeq ($(CONFIG_SND_OSSEMUL),y)
|
||||
snd-objs += sound_oss.o info_oss.o
|
||||
endif
|
||||
|
||||
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
|
||||
pcm_memory.o
|
||||
|
||||
snd-page-alloc-objs := memalloc.o sgbuf.o
|
||||
|
||||
snd-rawmidi-objs := rawmidi.o
|
||||
snd-timer-objs := timer.o
|
||||
snd-rtctimer-objs := rtctimer.o
|
||||
snd-hwdep-objs := hwdep.o
|
||||
|
||||
obj-$(CONFIG_SND) += snd.o
|
||||
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
|
||||
obj-$(CONFIG_SND_TIMER) += snd-timer.o
|
||||
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
|
||||
obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
|
||||
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
|
||||
|
||||
obj-$(CONFIG_SND_OSSEMUL) += oss/
|
||||
obj-$(CONFIG_SND_SEQUENCER) += seq/
|
||||
1375
sound/core/control.c
Normal file
1375
sound/core/control.c
Normal file
File diff suppressed because it is too large
Load Diff
412
sound/core/control_compat.c
Normal file
412
sound/core/control_compat.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* compat ioctls for control API
|
||||
*
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* this file included from control.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct sndrv_ctl_elem_list32 {
|
||||
u32 offset;
|
||||
u32 space;
|
||||
u32 used;
|
||||
u32 count;
|
||||
u32 pids;
|
||||
unsigned char reserved[50];
|
||||
} /* don't set packed attribute here */;
|
||||
|
||||
static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32)
|
||||
{
|
||||
struct sndrv_ctl_elem_list __user *data;
|
||||
compat_caddr_t ptr;
|
||||
int err;
|
||||
|
||||
data = compat_alloc_user_space(sizeof(*data));
|
||||
|
||||
/* offset, space, used, count */
|
||||
if (copy_in_user(data, data32, 4 * sizeof(u32)))
|
||||
return -EFAULT;
|
||||
/* pids */
|
||||
if (get_user(ptr, &data32->pids) ||
|
||||
put_user(compat_ptr(ptr), &data->pids))
|
||||
return -EFAULT;
|
||||
err = snd_ctl_elem_list(card, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* copy the result */
|
||||
if (copy_in_user(data32, data, 4 * sizeof(u32)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* control element info
|
||||
* it uses union, so the things are not easy..
|
||||
*/
|
||||
|
||||
struct sndrv_ctl_elem_info32 {
|
||||
struct sndrv_ctl_elem_id id; // the size of struct is same
|
||||
s32 type;
|
||||
u32 access;
|
||||
u32 count;
|
||||
s32 owner;
|
||||
union {
|
||||
struct {
|
||||
s32 min;
|
||||
s32 max;
|
||||
s32 step;
|
||||
} integer;
|
||||
struct {
|
||||
u64 min;
|
||||
u64 max;
|
||||
u64 step;
|
||||
} integer64;
|
||||
struct {
|
||||
u32 items;
|
||||
u32 item;
|
||||
char name[64];
|
||||
} enumerated;
|
||||
unsigned char reserved[128];
|
||||
} value;
|
||||
unsigned char reserved[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32)
|
||||
{
|
||||
struct sndrv_ctl_elem_info *data;
|
||||
int err;
|
||||
|
||||
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
|
||||
if (! data)
|
||||
return -ENOMEM;
|
||||
|
||||
err = -EFAULT;
|
||||
/* copy id */
|
||||
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
|
||||
goto error;
|
||||
/* we need to copy the item index.
|
||||
* hope this doesn't break anything..
|
||||
*/
|
||||
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
|
||||
goto error;
|
||||
err = snd_ctl_elem_info(ctl, data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
/* restore info to 32bit */
|
||||
err = -EFAULT;
|
||||
/* id, type, access, count */
|
||||
if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
|
||||
copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
|
||||
goto error;
|
||||
if (put_user(data->owner, &data32->owner))
|
||||
goto error;
|
||||
switch (data->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
if (put_user(data->value.integer.min, &data32->value.integer.min) ||
|
||||
put_user(data->value.integer.max, &data32->value.integer.max) ||
|
||||
put_user(data->value.integer.step, &data32->value.integer.step))
|
||||
goto error;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
|
||||
if (copy_to_user(&data32->value.integer64,
|
||||
&data->value.integer64,
|
||||
sizeof(data->value.integer64)))
|
||||
goto error;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
if (copy_to_user(&data32->value.enumerated,
|
||||
&data->value.enumerated,
|
||||
sizeof(data->value.enumerated)))
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
err = 0;
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* read / write */
|
||||
struct sndrv_ctl_elem_value32 {
|
||||
struct sndrv_ctl_elem_id id;
|
||||
unsigned int indirect; /* bit-field causes misalignment */
|
||||
union {
|
||||
s32 integer[128];
|
||||
unsigned char data[512];
|
||||
#ifndef CONFIG_X86_64
|
||||
s64 integer64[64];
|
||||
#endif
|
||||
} value;
|
||||
unsigned char reserved[128];
|
||||
};
|
||||
|
||||
|
||||
/* get the value type and count of the control */
|
||||
static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp)
|
||||
{
|
||||
snd_kcontrol_t *kctl;
|
||||
snd_ctl_elem_info_t info;
|
||||
int err;
|
||||
|
||||
down_read(&card->controls_rwsem);
|
||||
kctl = snd_ctl_find_id(card, id);
|
||||
if (! kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return -ENXIO;
|
||||
}
|
||||
info.id = *id;
|
||||
err = kctl->info(kctl, &info);
|
||||
up_read(&card->controls_rwsem);
|
||||
if (err >= 0) {
|
||||
err = info.type;
|
||||
*countp = info.count;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_elem_size(int type, int count)
|
||||
{
|
||||
switch (type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
|
||||
return sizeof(s64) * count;
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
return sizeof(int) * count;
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
return 512;
|
||||
case SNDRV_CTL_ELEM_TYPE_IEC958:
|
||||
return sizeof(struct sndrv_aes_iec958);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int copy_ctl_value_from_user(snd_card_t *card,
|
||||
struct sndrv_ctl_elem_value *data,
|
||||
struct sndrv_ctl_elem_value32 __user *data32,
|
||||
int *typep, int *countp)
|
||||
{
|
||||
int i, type, count, size;
|
||||
unsigned int indirect;
|
||||
|
||||
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
|
||||
return -EFAULT;
|
||||
if (get_user(indirect, &data32->indirect))
|
||||
return -EFAULT;
|
||||
if (indirect)
|
||||
return -EINVAL;
|
||||
type = get_ctl_type(card, &data->id, &count);
|
||||
if (type < 0)
|
||||
return type;
|
||||
|
||||
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
||||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
||||
for (i = 0; i < count; i++) {
|
||||
int val;
|
||||
if (get_user(val, &data32->value.integer[i]))
|
||||
return -EFAULT;
|
||||
data->value.integer.value[i] = val;
|
||||
}
|
||||
} else {
|
||||
size = get_elem_size(type, count);
|
||||
if (size < 0) {
|
||||
printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (copy_from_user(data->value.bytes.data,
|
||||
data32->value.data, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*typep = type;
|
||||
*countp = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* restore the value to 32bit */
|
||||
static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32,
|
||||
struct sndrv_ctl_elem_value *data,
|
||||
int type, int count)
|
||||
{
|
||||
int i, size;
|
||||
|
||||
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
|
||||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
|
||||
for (i = 0; i < count; i++) {
|
||||
int val;
|
||||
val = data->value.integer.value[i];
|
||||
if (put_user(val, &data32->value.integer[i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
size = get_elem_size(type, count);
|
||||
if (copy_to_user(data32->value.data,
|
||||
data->value.bytes.data, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ctl_elem_read_user_compat(snd_card_t *card,
|
||||
struct sndrv_ctl_elem_value32 __user *data32)
|
||||
{
|
||||
struct sndrv_ctl_elem_value *data;
|
||||
int err, type, count;
|
||||
|
||||
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
|
||||
goto error;
|
||||
if ((err = snd_ctl_elem_read(card, data)) < 0)
|
||||
goto error;
|
||||
err = copy_ctl_value_to_user(data32, data, type, count);
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file,
|
||||
struct sndrv_ctl_elem_value32 __user *data32)
|
||||
{
|
||||
struct sndrv_ctl_elem_value *data;
|
||||
int err, type, count;
|
||||
|
||||
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0)
|
||||
goto error;
|
||||
if ((err = snd_ctl_elem_write(file->card, file, data)) < 0)
|
||||
goto error;
|
||||
err = copy_ctl_value_to_user(data32, data, type, count);
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* add or replace a user control */
|
||||
static int snd_ctl_elem_add_compat(snd_ctl_file_t *file,
|
||||
struct sndrv_ctl_elem_info32 __user *data32,
|
||||
int replace)
|
||||
{
|
||||
struct sndrv_ctl_elem_info *data;
|
||||
int err;
|
||||
|
||||
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
|
||||
if (! data)
|
||||
return -ENOMEM;
|
||||
|
||||
err = -EFAULT;
|
||||
/* id, type, access, count */ \
|
||||
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
|
||||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
|
||||
goto error;
|
||||
if (get_user(data->owner, &data32->owner) ||
|
||||
get_user(data->type, &data32->type))
|
||||
goto error;
|
||||
switch (data->type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER:
|
||||
if (get_user(data->value.integer.min, &data32->value.integer.min) ||
|
||||
get_user(data->value.integer.max, &data32->value.integer.max) ||
|
||||
get_user(data->value.integer.step, &data32->value.integer.step))
|
||||
goto error;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
|
||||
if (copy_from_user(&data->value.integer64,
|
||||
&data32->value.integer64,
|
||||
sizeof(data->value.integer64)))
|
||||
goto error;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
if (copy_from_user(&data->value.enumerated,
|
||||
&data32->value.enumerated,
|
||||
sizeof(data->value.enumerated)))
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
err = snd_ctl_elem_add(file, data, replace);
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
enum {
|
||||
SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
|
||||
SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
|
||||
SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
|
||||
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
|
||||
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32),
|
||||
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32),
|
||||
};
|
||||
|
||||
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_ctl_file_t *ctl;
|
||||
struct list_head *list;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
int err;
|
||||
|
||||
ctl = file->private_data;
|
||||
snd_assert(ctl && ctl->card, return -ENXIO);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_CTL_IOCTL_PVERSION:
|
||||
case SNDRV_CTL_IOCTL_CARD_INFO:
|
||||
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
|
||||
case SNDRV_CTL_IOCTL_POWER:
|
||||
case SNDRV_CTL_IOCTL_POWER_STATE:
|
||||
case SNDRV_CTL_IOCTL_ELEM_LOCK:
|
||||
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
|
||||
return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
|
||||
case SNDRV_CTL_IOCTL_ELEM_LIST32:
|
||||
return snd_ctl_elem_list_compat(ctl->card, argp);
|
||||
case SNDRV_CTL_IOCTL_ELEM_INFO32:
|
||||
return snd_ctl_elem_info_compat(ctl, argp);
|
||||
case SNDRV_CTL_IOCTL_ELEM_READ32:
|
||||
return snd_ctl_elem_read_user_compat(ctl->card, argp);
|
||||
case SNDRV_CTL_IOCTL_ELEM_WRITE32:
|
||||
return snd_ctl_elem_write_user_compat(ctl, argp);
|
||||
case SNDRV_CTL_IOCTL_ELEM_ADD32:
|
||||
return snd_ctl_elem_add_compat(ctl, argp, 0);
|
||||
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
|
||||
return snd_ctl_elem_add_compat(ctl, argp, 1);
|
||||
}
|
||||
|
||||
down_read(&snd_ioctl_rwsem);
|
||||
list_for_each(list, &snd_control_compat_ioctls) {
|
||||
snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list);
|
||||
if (p->fioctl) {
|
||||
err = p->fioctl(ctl->card, ctl, cmd, arg);
|
||||
if (err != -ENOIOCTLCMD) {
|
||||
up_read(&snd_ioctl_rwsem);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
up_read(&snd_ioctl_rwsem);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
240
sound/core/device.c
Normal file
240
sound/core/device.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Device management routines
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
/**
|
||||
* snd_device_new - create an ALSA device component
|
||||
* @card: the card instance
|
||||
* @type: the device type, SNDRV_DEV_TYPE_XXX
|
||||
* @device_data: the data pointer of this device
|
||||
* @ops: the operator table
|
||||
*
|
||||
* Creates a new device component for the given data pointer.
|
||||
* The device will be assigned to the card and managed together
|
||||
* by the card.
|
||||
*
|
||||
* The data pointer plays a role as the identifier, too, so the
|
||||
* pointer address must be unique and unchanged.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_device_new(snd_card_t *card, snd_device_type_t type,
|
||||
void *device_data, snd_device_ops_t *ops)
|
||||
{
|
||||
snd_device_t *dev;
|
||||
|
||||
snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO);
|
||||
dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
|
||||
if (dev == NULL)
|
||||
return -ENOMEM;
|
||||
dev->card = card;
|
||||
dev->type = type;
|
||||
dev->state = SNDRV_DEV_BUILD;
|
||||
dev->device_data = device_data;
|
||||
dev->ops = ops;
|
||||
list_add(&dev->list, &card->devices); /* add to the head of list */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_device_free - release the device from the card
|
||||
* @card: the card instance
|
||||
* @device_data: the data pointer to release
|
||||
*
|
||||
* Removes the device from the list on the card and invokes the
|
||||
* callback, dev_unregister or dev_free, corresponding to the state.
|
||||
* Then release the device.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure or if the
|
||||
* device not found.
|
||||
*/
|
||||
int snd_device_free(snd_card_t *card, void *device_data)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
snd_assert(device_data != NULL, return -ENXIO);
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->device_data != device_data)
|
||||
continue;
|
||||
/* unlink */
|
||||
list_del(&dev->list);
|
||||
if ((dev->state == SNDRV_DEV_REGISTERED || dev->state == SNDRV_DEV_DISCONNECTED) &&
|
||||
dev->ops->dev_unregister) {
|
||||
if (dev->ops->dev_unregister(dev))
|
||||
snd_printk(KERN_ERR "device unregister failure\n");
|
||||
} else {
|
||||
if (dev->ops->dev_free) {
|
||||
if (dev->ops->dev_free(dev))
|
||||
snd_printk(KERN_ERR "device free failure\n");
|
||||
}
|
||||
}
|
||||
kfree(dev);
|
||||
return 0;
|
||||
}
|
||||
snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_device_free - disconnect the device
|
||||
* @card: the card instance
|
||||
* @device_data: the data pointer to disconnect
|
||||
*
|
||||
* Turns the device into the disconnection state, invoking
|
||||
* dev_disconnect callback, if the device was already registered.
|
||||
*
|
||||
* Usually called from snd_card_disconnect().
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure or if the
|
||||
* device not found.
|
||||
*/
|
||||
int snd_device_disconnect(snd_card_t *card, void *device_data)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
snd_assert(device_data != NULL, return -ENXIO);
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->device_data != device_data)
|
||||
continue;
|
||||
if (dev->state == SNDRV_DEV_REGISTERED && dev->ops->dev_disconnect) {
|
||||
if (dev->ops->dev_disconnect(dev))
|
||||
snd_printk(KERN_ERR "device disconnect failure\n");
|
||||
dev->state = SNDRV_DEV_DISCONNECTED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
snd_printd("device disconnect %p (from %p), not found\n", device_data, __builtin_return_address(0));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_device_register - register the device
|
||||
* @card: the card instance
|
||||
* @device_data: the data pointer to register
|
||||
*
|
||||
* Registers the device which was already created via
|
||||
* snd_device_new(). Usually this is called from snd_card_register(),
|
||||
* but it can be called later if any new devices are created after
|
||||
* invocation of snd_card_register().
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure or if the
|
||||
* device not found.
|
||||
*/
|
||||
int snd_device_register(snd_card_t *card, void *device_data)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
int err;
|
||||
|
||||
snd_assert(card != NULL && device_data != NULL, return -ENXIO);
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->device_data != device_data)
|
||||
continue;
|
||||
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
|
||||
if ((err = dev->ops->dev_register(dev)) < 0)
|
||||
return err;
|
||||
dev->state = SNDRV_DEV_REGISTERED;
|
||||
return 0;
|
||||
}
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_BUG();
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* register all the devices on the card.
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_device_register_all(snd_card_t *card)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_device_t *dev;
|
||||
int err;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
|
||||
if ((err = dev->ops->dev_register(dev)) < 0)
|
||||
return err;
|
||||
dev->state = SNDRV_DEV_REGISTERED;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* disconnect all the devices on the card.
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_device_disconnect_all(snd_card_t *card)
|
||||
{
|
||||
snd_device_t *dev;
|
||||
struct list_head *list;
|
||||
int err = 0;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (snd_device_disconnect(card, dev->device_data) < 0)
|
||||
err = -ENXIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* release all the devices on the card.
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_device_free_all(snd_card_t *card, snd_device_cmd_t cmd)
|
||||
{
|
||||
snd_device_t *dev;
|
||||
struct list_head *list;
|
||||
int err;
|
||||
unsigned int range_low, range_high;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
|
||||
range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
|
||||
__again:
|
||||
list_for_each(list, &card->devices) {
|
||||
dev = snd_device(list);
|
||||
if (dev->type >= range_low && dev->type <= range_high) {
|
||||
if ((err = snd_device_free(card, dev->device_data)) < 0)
|
||||
return err;
|
||||
goto __again;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
524
sound/core/hwdep.c
Normal file
524
sound/core/hwdep.c
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* Hardware dependent layer
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Hardware dependent layer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static snd_hwdep_t *snd_hwdep_devices[SNDRV_CARDS * SNDRV_MINOR_HWDEPS];
|
||||
|
||||
static DECLARE_MUTEX(register_mutex);
|
||||
|
||||
static int snd_hwdep_free(snd_hwdep_t *hwdep);
|
||||
static int snd_hwdep_dev_free(snd_device_t *device);
|
||||
static int snd_hwdep_dev_register(snd_device_t *device);
|
||||
static int snd_hwdep_dev_unregister(snd_device_t *device);
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
if (hw->ops.llseek)
|
||||
return hw->ops.llseek(hw, file, offset, orig);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static ssize_t snd_hwdep_read(struct file * file, char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
if (hw->ops.read)
|
||||
return hw->ops.read(hw, buf, count, offset);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static ssize_t snd_hwdep_write(struct file * file, const char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
if (hw->ops.write)
|
||||
return hw->ops.write(hw, buf, count, offset);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int snd_hwdep_open(struct inode *inode, struct file * file)
|
||||
{
|
||||
int major = imajor(inode);
|
||||
int cardnum;
|
||||
int device;
|
||||
snd_hwdep_t *hw;
|
||||
int err;
|
||||
wait_queue_t wait;
|
||||
|
||||
switch (major) {
|
||||
case CONFIG_SND_MAJOR:
|
||||
cardnum = SNDRV_MINOR_CARD(iminor(inode));
|
||||
device = SNDRV_MINOR_DEVICE(iminor(inode)) - SNDRV_MINOR_HWDEP;
|
||||
break;
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
case SOUND_MAJOR:
|
||||
cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));
|
||||
device = 0;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
cardnum %= SNDRV_CARDS;
|
||||
device %= SNDRV_MINOR_HWDEPS;
|
||||
hw = snd_hwdep_devices[(cardnum * SNDRV_MINOR_HWDEPS) + device];
|
||||
if (hw == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!hw->ops.open)
|
||||
return -ENXIO;
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
if (major == SOUND_MAJOR && hw->oss_type < 0)
|
||||
return -ENXIO;
|
||||
#endif
|
||||
|
||||
if (!try_module_get(hw->card->module))
|
||||
return -EFAULT;
|
||||
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&hw->open_wait, &wait);
|
||||
down(&hw->open_mutex);
|
||||
while (1) {
|
||||
if (hw->exclusive && hw->used > 0) {
|
||||
err = -EBUSY;
|
||||
break;
|
||||
}
|
||||
err = hw->ops.open(hw, file);
|
||||
if (err >= 0)
|
||||
break;
|
||||
if (err == -EAGAIN) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
err = -EBUSY;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
up(&hw->open_mutex);
|
||||
schedule();
|
||||
down(&hw->open_mutex);
|
||||
if (signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
remove_wait_queue(&hw->open_wait, &wait);
|
||||
if (err >= 0) {
|
||||
err = snd_card_file_add(hw->card, file);
|
||||
if (err >= 0) {
|
||||
file->private_data = hw;
|
||||
hw->used++;
|
||||
} else {
|
||||
if (hw->ops.release)
|
||||
hw->ops.release(hw, file);
|
||||
}
|
||||
}
|
||||
up(&hw->open_mutex);
|
||||
if (err < 0)
|
||||
module_put(hw->card->module);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_hwdep_release(struct inode *inode, struct file * file)
|
||||
{
|
||||
int err = -ENXIO;
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
down(&hw->open_mutex);
|
||||
if (hw->ops.release) {
|
||||
err = hw->ops.release(hw, file);
|
||||
wake_up(&hw->open_wait);
|
||||
}
|
||||
if (hw->used > 0)
|
||||
hw->used--;
|
||||
snd_card_file_remove(hw->card, file);
|
||||
up(&hw->open_mutex);
|
||||
module_put(hw->card->module);
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
if (hw->ops.poll)
|
||||
return hw->ops.poll(hw, file, wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_info(snd_hwdep_t *hw, snd_hwdep_info_t __user *_info)
|
||||
{
|
||||
snd_hwdep_info_t info;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.card = hw->card->number;
|
||||
strlcpy(info.id, hw->id, sizeof(info.id));
|
||||
strlcpy(info.name, hw->name, sizeof(info.name));
|
||||
info.iface = hw->iface;
|
||||
if (copy_to_user(_info, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t __user *_info)
|
||||
{
|
||||
snd_hwdep_dsp_status_t info;
|
||||
int err;
|
||||
|
||||
if (! hw->ops.dsp_status)
|
||||
return -ENXIO;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.dsp_loaded = hw->dsp_loaded;
|
||||
if ((err = hw->ops.dsp_status(hw, &info)) < 0)
|
||||
return err;
|
||||
if (copy_to_user(_info, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t __user *_info)
|
||||
{
|
||||
snd_hwdep_dsp_image_t info;
|
||||
int err;
|
||||
|
||||
if (! hw->ops.dsp_load)
|
||||
return -ENXIO;
|
||||
memset(&info, 0, sizeof(info));
|
||||
if (copy_from_user(&info, _info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
/* check whether the dsp was already loaded */
|
||||
if (hw->dsp_loaded & (1 << info.index))
|
||||
return -EBUSY;
|
||||
if (!access_ok(VERIFY_READ, info.image, info.length))
|
||||
return -EFAULT;
|
||||
err = hw->ops.dsp_load(hw, &info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
hw->dsp_loaded |= (1 << info.index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
void __user *argp = (void __user *)arg;
|
||||
switch (cmd) {
|
||||
case SNDRV_HWDEP_IOCTL_PVERSION:
|
||||
return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);
|
||||
case SNDRV_HWDEP_IOCTL_INFO:
|
||||
return snd_hwdep_info(hw, argp);
|
||||
case SNDRV_HWDEP_IOCTL_DSP_STATUS:
|
||||
return snd_hwdep_dsp_status(hw, argp);
|
||||
case SNDRV_HWDEP_IOCTL_DSP_LOAD:
|
||||
return snd_hwdep_dsp_load(hw, argp);
|
||||
}
|
||||
if (hw->ops.ioctl)
|
||||
return hw->ops.ioctl(hw, file, cmd, arg);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
if (hw->ops.mmap)
|
||||
return hw->ops.mmap(hw, file, vma);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int snd_hwdep_control_ioctl(snd_card_t * card, snd_ctl_file_t * control,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
tmp = card->number * SNDRV_MINOR_HWDEPS;
|
||||
switch (cmd) {
|
||||
case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE:
|
||||
{
|
||||
int device;
|
||||
|
||||
if (get_user(device, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
device = device < 0 ? 0 : device + 1;
|
||||
while (device < SNDRV_MINOR_HWDEPS) {
|
||||
if (snd_hwdep_devices[tmp + device])
|
||||
break;
|
||||
device++;
|
||||
}
|
||||
if (device >= SNDRV_MINOR_HWDEPS)
|
||||
device = -1;
|
||||
if (put_user(device, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case SNDRV_CTL_IOCTL_HWDEP_INFO:
|
||||
{
|
||||
snd_hwdep_info_t __user *info = (snd_hwdep_info_t __user *)arg;
|
||||
int device;
|
||||
snd_hwdep_t *hwdep;
|
||||
|
||||
if (get_user(device, &info->device))
|
||||
return -EFAULT;
|
||||
if (device < 0 || device >= SNDRV_MINOR_HWDEPS)
|
||||
return -ENXIO;
|
||||
hwdep = snd_hwdep_devices[tmp + device];
|
||||
if (hwdep == NULL)
|
||||
return -ENXIO;
|
||||
return snd_hwdep_info(hwdep, info);
|
||||
}
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include "hwdep_compat.c"
|
||||
#else
|
||||
#define snd_hwdep_ioctl_compat NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static struct file_operations snd_hwdep_f_ops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = snd_hwdep_llseek,
|
||||
.read = snd_hwdep_read,
|
||||
.write = snd_hwdep_write,
|
||||
.open = snd_hwdep_open,
|
||||
.release = snd_hwdep_release,
|
||||
.poll = snd_hwdep_poll,
|
||||
.unlocked_ioctl = snd_hwdep_ioctl,
|
||||
.compat_ioctl = snd_hwdep_ioctl_compat,
|
||||
.mmap = snd_hwdep_mmap,
|
||||
};
|
||||
|
||||
static snd_minor_t snd_hwdep_reg =
|
||||
{
|
||||
.comment = "hardware dependent",
|
||||
.f_ops = &snd_hwdep_f_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_hwdep_new - create a new hwdep instance
|
||||
* @card: the card instance
|
||||
* @id: the id string
|
||||
* @device: the device index (zero-based)
|
||||
* @rhwdep: the pointer to store the new hwdep instance
|
||||
*
|
||||
* Creates a new hwdep instance with the given index on the card.
|
||||
* The callbacks (hwdep->ops) must be set on the returned instance
|
||||
* after this call manually by the caller.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep)
|
||||
{
|
||||
snd_hwdep_t *hwdep;
|
||||
int err;
|
||||
static snd_device_ops_t ops = {
|
||||
.dev_free = snd_hwdep_dev_free,
|
||||
.dev_register = snd_hwdep_dev_register,
|
||||
.dev_unregister = snd_hwdep_dev_unregister
|
||||
};
|
||||
|
||||
snd_assert(rhwdep != NULL, return -EINVAL);
|
||||
*rhwdep = NULL;
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
hwdep = kcalloc(1, sizeof(*hwdep), GFP_KERNEL);
|
||||
if (hwdep == NULL)
|
||||
return -ENOMEM;
|
||||
hwdep->card = card;
|
||||
hwdep->device = device;
|
||||
if (id) {
|
||||
strlcpy(hwdep->id, id, sizeof(hwdep->id));
|
||||
}
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
hwdep->oss_type = -1;
|
||||
#endif
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
|
||||
snd_hwdep_free(hwdep);
|
||||
return err;
|
||||
}
|
||||
init_waitqueue_head(&hwdep->open_wait);
|
||||
init_MUTEX(&hwdep->open_mutex);
|
||||
*rhwdep = hwdep;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_free(snd_hwdep_t *hwdep)
|
||||
{
|
||||
snd_assert(hwdep != NULL, return -ENXIO);
|
||||
if (hwdep->private_free)
|
||||
hwdep->private_free(hwdep);
|
||||
kfree(hwdep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_dev_free(snd_device_t *device)
|
||||
{
|
||||
snd_hwdep_t *hwdep = device->device_data;
|
||||
return snd_hwdep_free(hwdep);
|
||||
}
|
||||
|
||||
static int snd_hwdep_dev_register(snd_device_t *device)
|
||||
{
|
||||
snd_hwdep_t *hwdep = device->device_data;
|
||||
int idx, err;
|
||||
char name[32];
|
||||
|
||||
down(®ister_mutex);
|
||||
idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
|
||||
if (snd_hwdep_devices[idx]) {
|
||||
up(®ister_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_hwdep_devices[idx] = hwdep;
|
||||
sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
|
||||
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
|
||||
hwdep->card, hwdep->device,
|
||||
&snd_hwdep_reg, name)) < 0) {
|
||||
snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
|
||||
hwdep->card->number, hwdep->device);
|
||||
snd_hwdep_devices[idx] = NULL;
|
||||
up(®ister_mutex);
|
||||
return err;
|
||||
}
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
hwdep->ossreg = 0;
|
||||
if (hwdep->oss_type >= 0) {
|
||||
if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
|
||||
snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
|
||||
} else {
|
||||
if (snd_register_oss_device(hwdep->oss_type,
|
||||
hwdep->card, hwdep->device,
|
||||
&snd_hwdep_reg, hwdep->oss_dev) < 0) {
|
||||
snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
|
||||
hwdep->card->number, hwdep->device);
|
||||
} else
|
||||
hwdep->ossreg = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
up(®ister_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_hwdep_dev_unregister(snd_device_t *device)
|
||||
{
|
||||
snd_hwdep_t *hwdep = device->device_data;
|
||||
int idx;
|
||||
|
||||
snd_assert(hwdep != NULL, return -ENXIO);
|
||||
down(®ister_mutex);
|
||||
idx = (hwdep->card->number * SNDRV_MINOR_HWDEPS) + hwdep->device;
|
||||
if (snd_hwdep_devices[idx] != hwdep) {
|
||||
up(®ister_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
if (hwdep->ossreg)
|
||||
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
|
||||
#endif
|
||||
snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
|
||||
snd_hwdep_devices[idx] = NULL;
|
||||
up(®ister_mutex);
|
||||
return snd_hwdep_free(hwdep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Info interface
|
||||
*/
|
||||
|
||||
static void snd_hwdep_proc_read(snd_info_entry_t *entry,
|
||||
snd_info_buffer_t * buffer)
|
||||
{
|
||||
int idx;
|
||||
snd_hwdep_t *hwdep;
|
||||
|
||||
down(®ister_mutex);
|
||||
for (idx = 0; idx < SNDRV_CARDS * SNDRV_MINOR_HWDEPS; idx++) {
|
||||
hwdep = snd_hwdep_devices[idx];
|
||||
if (hwdep == NULL)
|
||||
continue;
|
||||
snd_iprintf(buffer, "%02i-%02i: %s\n",
|
||||
idx / SNDRV_MINOR_HWDEPS,
|
||||
idx % SNDRV_MINOR_HWDEPS,
|
||||
hwdep->name);
|
||||
}
|
||||
up(®ister_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* ENTRY functions
|
||||
*/
|
||||
|
||||
static snd_info_entry_t *snd_hwdep_proc_entry = NULL;
|
||||
|
||||
static int __init alsa_hwdep_init(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
memset(snd_hwdep_devices, 0, sizeof(snd_hwdep_devices));
|
||||
if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
|
||||
entry->c.text.read_size = 512;
|
||||
entry->c.text.read = snd_hwdep_proc_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
snd_hwdep_proc_entry = entry;
|
||||
snd_ctl_register_ioctl(snd_hwdep_control_ioctl);
|
||||
snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_hwdep_exit(void)
|
||||
{
|
||||
snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl);
|
||||
snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl);
|
||||
if (snd_hwdep_proc_entry) {
|
||||
snd_info_unregister(snd_hwdep_proc_entry);
|
||||
snd_hwdep_proc_entry = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(alsa_hwdep_init)
|
||||
module_exit(alsa_hwdep_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_hwdep_new);
|
||||
77
sound/core/hwdep_compat.c
Normal file
77
sound/core/hwdep_compat.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 32bit -> 64bit ioctl wrapper for hwdep API
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file is included from hwdep.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct sndrv_hwdep_dsp_image32 {
|
||||
u32 index;
|
||||
unsigned char name[64];
|
||||
u32 image; /* pointer */
|
||||
u32 length;
|
||||
u32 driver_data;
|
||||
} /* don't set packed attribute here */;
|
||||
|
||||
static int snd_hwdep_dsp_load_compat(snd_hwdep_t *hw,
|
||||
struct sndrv_hwdep_dsp_image32 __user *src)
|
||||
{
|
||||
struct sndrv_hwdep_dsp_image *dst;
|
||||
compat_caddr_t ptr;
|
||||
u32 val;
|
||||
|
||||
dst = compat_alloc_user_space(sizeof(*dst));
|
||||
|
||||
/* index and name */
|
||||
if (copy_in_user(dst, src, 4 + 64))
|
||||
return -EFAULT;
|
||||
if (get_user(ptr, &src->image) ||
|
||||
put_user(compat_ptr(ptr), &dst->image))
|
||||
return -EFAULT;
|
||||
if (get_user(val, &src->length) ||
|
||||
put_user(val, &dst->length))
|
||||
return -EFAULT;
|
||||
if (get_user(val, &src->driver_data) ||
|
||||
put_user(val, &dst->driver_data))
|
||||
return -EFAULT;
|
||||
|
||||
return snd_hwdep_dsp_load(hw, dst);
|
||||
}
|
||||
|
||||
enum {
|
||||
SNDRV_HWDEP_IOCTL_DSP_LOAD32 = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32)
|
||||
};
|
||||
|
||||
static long snd_hwdep_ioctl_compat(struct file * file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_hwdep_t *hw = file->private_data;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
switch (cmd) {
|
||||
case SNDRV_HWDEP_IOCTL_PVERSION:
|
||||
case SNDRV_HWDEP_IOCTL_INFO:
|
||||
case SNDRV_HWDEP_IOCTL_DSP_STATUS:
|
||||
return snd_hwdep_ioctl(file, cmd, (unsigned long)argp);
|
||||
case SNDRV_HWDEP_IOCTL_DSP_LOAD32:
|
||||
return snd_hwdep_dsp_load_compat(hw, argp);
|
||||
}
|
||||
if (hw->ops.ioctl_compat)
|
||||
return hw->ops.ioctl_compat(hw, file, cmd, arg);
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
989
sound/core/info.c
Normal file
989
sound/core/info.c
Normal file
@@ -0,0 +1,989 @@
|
||||
/*
|
||||
* Information interface for ALSA driver
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/version.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
int snd_info_check_reserved_words(const char *str)
|
||||
{
|
||||
static char *reserved[] =
|
||||
{
|
||||
"version",
|
||||
"meminfo",
|
||||
"memdebug",
|
||||
"detect",
|
||||
"devices",
|
||||
"oss",
|
||||
"cards",
|
||||
"timers",
|
||||
"synth",
|
||||
"pcm",
|
||||
"seq",
|
||||
NULL
|
||||
};
|
||||
char **xstr = reserved;
|
||||
|
||||
while (*xstr) {
|
||||
if (!strcmp(*xstr, str))
|
||||
return 0;
|
||||
xstr++;
|
||||
}
|
||||
if (!strncmp(str, "card", 4))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static DECLARE_MUTEX(info_mutex);
|
||||
|
||||
typedef struct _snd_info_private_data {
|
||||
snd_info_buffer_t *rbuffer;
|
||||
snd_info_buffer_t *wbuffer;
|
||||
snd_info_entry_t *entry;
|
||||
void *file_private_data;
|
||||
} snd_info_private_data_t;
|
||||
|
||||
static int snd_info_version_init(void);
|
||||
static int snd_info_version_done(void);
|
||||
|
||||
|
||||
/**
|
||||
* snd_iprintf - printf on the procfs buffer
|
||||
* @buffer: the procfs buffer
|
||||
* @fmt: the printf format
|
||||
*
|
||||
* Outputs the string on the procfs buffer just like printf().
|
||||
*
|
||||
* Returns the size of output string.
|
||||
*/
|
||||
int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
|
||||
{
|
||||
va_list args;
|
||||
int len, res;
|
||||
|
||||
if (buffer->stop || buffer->error)
|
||||
return 0;
|
||||
len = buffer->len - buffer->size;
|
||||
va_start(args, fmt);
|
||||
res = vsnprintf(buffer->curr, len, fmt, args);
|
||||
va_end(args);
|
||||
if (res >= len) {
|
||||
buffer->stop = 1;
|
||||
return 0;
|
||||
}
|
||||
buffer->curr += res;
|
||||
buffer->size += res;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static struct proc_dir_entry *snd_proc_root = NULL;
|
||||
snd_info_entry_t *snd_seq_root = NULL;
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
snd_info_entry_t *snd_oss_root = NULL;
|
||||
#endif
|
||||
|
||||
static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
|
||||
{
|
||||
de->owner = THIS_MODULE;
|
||||
}
|
||||
|
||||
static void snd_remove_proc_entry(struct proc_dir_entry *parent,
|
||||
struct proc_dir_entry *de)
|
||||
{
|
||||
if (de)
|
||||
remove_proc_entry(de->name, parent);
|
||||
}
|
||||
|
||||
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
loff_t ret;
|
||||
|
||||
data = file->private_data;
|
||||
entry = data->entry;
|
||||
lock_kernel();
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
switch (orig) {
|
||||
case 0: /* SEEK_SET */
|
||||
file->f_pos = offset;
|
||||
ret = file->f_pos;
|
||||
goto out;
|
||||
case 1: /* SEEK_CUR */
|
||||
file->f_pos += offset;
|
||||
ret = file->f_pos;
|
||||
goto out;
|
||||
case 2: /* SEEK_END */
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->llseek) {
|
||||
ret = entry->c.ops->llseek(entry,
|
||||
data->file_private_data,
|
||||
file, offset, orig);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ret = -ENXIO;
|
||||
out:
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t * offset)
|
||||
{
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
snd_info_buffer_t *buf;
|
||||
size_t size = 0;
|
||||
loff_t pos;
|
||||
|
||||
data = file->private_data;
|
||||
snd_assert(data != NULL, return -ENXIO);
|
||||
pos = *offset;
|
||||
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
|
||||
return -EIO;
|
||||
if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
|
||||
return -EIO;
|
||||
entry = data->entry;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
buf = data->rbuffer;
|
||||
if (buf == NULL)
|
||||
return -EIO;
|
||||
if (pos >= buf->size)
|
||||
return 0;
|
||||
size = buf->size - pos;
|
||||
size = min(count, size);
|
||||
if (copy_to_user(buffer, buf->buffer + pos, size))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->read)
|
||||
size = entry->c.ops->read(entry,
|
||||
data->file_private_data,
|
||||
file, buffer, count, pos);
|
||||
break;
|
||||
}
|
||||
if ((ssize_t) size > 0)
|
||||
*offset = pos + size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t * offset)
|
||||
{
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
snd_info_buffer_t *buf;
|
||||
size_t size = 0;
|
||||
loff_t pos;
|
||||
|
||||
data = file->private_data;
|
||||
snd_assert(data != NULL, return -ENXIO);
|
||||
entry = data->entry;
|
||||
pos = *offset;
|
||||
if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
|
||||
return -EIO;
|
||||
if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
|
||||
return -EIO;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
buf = data->wbuffer;
|
||||
if (buf == NULL)
|
||||
return -EIO;
|
||||
if (pos >= buf->len)
|
||||
return -ENOMEM;
|
||||
size = buf->len - pos;
|
||||
size = min(count, size);
|
||||
if (copy_from_user(buf->buffer + pos, buffer, size))
|
||||
return -EFAULT;
|
||||
if ((long)buf->size < pos + size)
|
||||
buf->size = pos + size;
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->write)
|
||||
size = entry->c.ops->write(entry,
|
||||
data->file_private_data,
|
||||
file, buffer, count, pos);
|
||||
break;
|
||||
}
|
||||
if ((ssize_t) size > 0)
|
||||
*offset = pos + size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static int snd_info_entry_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
snd_info_private_data_t *data;
|
||||
snd_info_buffer_t *buffer;
|
||||
struct proc_dir_entry *p;
|
||||
int mode, err;
|
||||
|
||||
down(&info_mutex);
|
||||
p = PDE(inode);
|
||||
entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
|
||||
if (entry == NULL || entry->disconnected) {
|
||||
up(&info_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!try_module_get(entry->module)) {
|
||||
err = -EFAULT;
|
||||
goto __error1;
|
||||
}
|
||||
mode = file->f_flags & O_ACCMODE;
|
||||
if (mode == O_RDONLY || mode == O_RDWR) {
|
||||
if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
|
||||
!entry->c.text.read_size) ||
|
||||
(entry->content == SNDRV_INFO_CONTENT_DATA &&
|
||||
entry->c.ops->read == NULL)) {
|
||||
err = -ENODEV;
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
if (mode == O_WRONLY || mode == O_RDWR) {
|
||||
if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
|
||||
!entry->c.text.write_size) ||
|
||||
(entry->content == SNDRV_INFO_CONTENT_DATA &&
|
||||
entry->c.ops->write == NULL)) {
|
||||
err = -ENODEV;
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
data->entry = entry;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
if (mode == O_RDONLY || mode == O_RDWR) {
|
||||
buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
kfree(data);
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
buffer->len = (entry->c.text.read_size +
|
||||
(PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
|
||||
buffer->buffer = vmalloc(buffer->len);
|
||||
if (buffer->buffer == NULL) {
|
||||
kfree(buffer);
|
||||
kfree(data);
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
buffer->curr = buffer->buffer;
|
||||
data->rbuffer = buffer;
|
||||
}
|
||||
if (mode == O_WRONLY || mode == O_RDWR) {
|
||||
buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
if (mode == O_RDWR) {
|
||||
vfree(data->rbuffer->buffer);
|
||||
kfree(data->rbuffer);
|
||||
}
|
||||
kfree(data);
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
buffer->len = (entry->c.text.write_size +
|
||||
(PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
|
||||
buffer->buffer = vmalloc(buffer->len);
|
||||
if (buffer->buffer == NULL) {
|
||||
if (mode == O_RDWR) {
|
||||
vfree(data->rbuffer->buffer);
|
||||
kfree(data->rbuffer);
|
||||
}
|
||||
kfree(buffer);
|
||||
kfree(data);
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
buffer->curr = buffer->buffer;
|
||||
data->wbuffer = buffer;
|
||||
}
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA: /* data */
|
||||
if (entry->c.ops->open) {
|
||||
if ((err = entry->c.ops->open(entry, mode,
|
||||
&data->file_private_data)) < 0) {
|
||||
kfree(data);
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
file->private_data = data;
|
||||
up(&info_mutex);
|
||||
if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
|
||||
(mode == O_RDONLY || mode == O_RDWR)) {
|
||||
if (entry->c.text.read) {
|
||||
down(&entry->access);
|
||||
entry->c.text.read(entry, data->rbuffer);
|
||||
up(&entry->access);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
module_put(entry->module);
|
||||
__error1:
|
||||
up(&info_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_info_entry_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
snd_info_private_data_t *data;
|
||||
int mode;
|
||||
|
||||
mode = file->f_flags & O_ACCMODE;
|
||||
data = file->private_data;
|
||||
entry = data->entry;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_TEXT:
|
||||
if (mode == O_RDONLY || mode == O_RDWR) {
|
||||
vfree(data->rbuffer->buffer);
|
||||
kfree(data->rbuffer);
|
||||
}
|
||||
if (mode == O_WRONLY || mode == O_RDWR) {
|
||||
if (entry->c.text.write) {
|
||||
entry->c.text.write(entry, data->wbuffer);
|
||||
if (data->wbuffer->error) {
|
||||
snd_printk(KERN_WARNING "data write error to %s (%i)\n",
|
||||
entry->name,
|
||||
data->wbuffer->error);
|
||||
}
|
||||
}
|
||||
vfree(data->wbuffer->buffer);
|
||||
kfree(data->wbuffer);
|
||||
}
|
||||
break;
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->release)
|
||||
entry->c.ops->release(entry, mode,
|
||||
data->file_private_data);
|
||||
break;
|
||||
}
|
||||
module_put(entry->module);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
|
||||
{
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
unsigned int mask;
|
||||
|
||||
data = file->private_data;
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
entry = data->entry;
|
||||
mask = 0;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->poll)
|
||||
return entry->c.ops->poll(entry,
|
||||
data->file_private_data,
|
||||
file, wait);
|
||||
if (entry->c.ops->read)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
if (entry->c.ops->write)
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
break;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
data = file->private_data;
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
entry = data->entry;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->ioctl)
|
||||
return entry->c.ops->ioctl(entry,
|
||||
data->file_private_data,
|
||||
file, cmd, arg);
|
||||
break;
|
||||
}
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/* FIXME: need to unlock BKL to allow preemption */
|
||||
static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int err;
|
||||
unlock_kernel();
|
||||
err = _snd_info_entry_ioctl(inode, file, cmd, arg);
|
||||
lock_kernel();
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
snd_info_private_data_t *data;
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
data = file->private_data;
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
entry = data->entry;
|
||||
switch (entry->content) {
|
||||
case SNDRV_INFO_CONTENT_DATA:
|
||||
if (entry->c.ops->mmap)
|
||||
return entry->c.ops->mmap(entry,
|
||||
data->file_private_data,
|
||||
inode, file, vma);
|
||||
break;
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static struct file_operations snd_info_entry_operations =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = snd_info_entry_llseek,
|
||||
.read = snd_info_entry_read,
|
||||
.write = snd_info_entry_write,
|
||||
.poll = snd_info_entry_poll,
|
||||
.ioctl = snd_info_entry_ioctl,
|
||||
.mmap = snd_info_entry_mmap,
|
||||
.open = snd_info_entry_open,
|
||||
.release = snd_info_entry_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_create_proc_entry - create a procfs entry
|
||||
* @name: the name of the proc file
|
||||
* @mode: the file permission bits, S_Ixxx
|
||||
* @parent: the parent proc-directory entry
|
||||
*
|
||||
* Creates a new proc file entry with the given name and permission
|
||||
* on the given directory.
|
||||
*
|
||||
* Returns the pointer of new instance or NULL on failure.
|
||||
*/
|
||||
static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
|
||||
struct proc_dir_entry *parent)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
p = create_proc_entry(name, mode, parent);
|
||||
if (p)
|
||||
snd_info_entry_prepare(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
int __init snd_info_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
|
||||
if (p == NULL)
|
||||
return -ENOMEM;
|
||||
snd_proc_root = p;
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_oss_root = entry;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_seq_root = entry;
|
||||
}
|
||||
#endif
|
||||
snd_info_version_init();
|
||||
snd_memory_info_init();
|
||||
snd_minor_info_init();
|
||||
snd_minor_info_oss_init();
|
||||
snd_card_info_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_info_done(void)
|
||||
{
|
||||
snd_card_info_done();
|
||||
snd_minor_info_oss_done();
|
||||
snd_minor_info_done();
|
||||
snd_memory_info_done();
|
||||
snd_info_version_done();
|
||||
if (snd_proc_root) {
|
||||
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
|
||||
if (snd_seq_root)
|
||||
snd_info_unregister(snd_seq_root);
|
||||
#endif
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
if (snd_oss_root)
|
||||
snd_info_unregister(snd_oss_root);
|
||||
#endif
|
||||
snd_remove_proc_entry(&proc_root, snd_proc_root);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* create a card proc file
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_info_card_create(snd_card_t * card)
|
||||
{
|
||||
char str[8];
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
|
||||
sprintf(str, "card%i", card->number);
|
||||
if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
|
||||
return -ENOMEM;
|
||||
entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
card->proc_root = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* register the card proc file
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_info_card_register(snd_card_t * card)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
|
||||
if (!strcmp(card->id, card->proc_root->name))
|
||||
return 0;
|
||||
|
||||
p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
|
||||
if (p == NULL)
|
||||
return -ENOMEM;
|
||||
card->proc_root_link = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* de-register the card proc file
|
||||
* called from init.c
|
||||
*/
|
||||
int snd_info_card_free(snd_card_t * card)
|
||||
{
|
||||
snd_assert(card != NULL, return -ENXIO);
|
||||
if (card->proc_root_link) {
|
||||
snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
|
||||
card->proc_root_link = NULL;
|
||||
}
|
||||
if (card->proc_root) {
|
||||
snd_info_unregister(card->proc_root);
|
||||
card->proc_root = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* snd_info_get_line - read one line from the procfs buffer
|
||||
* @buffer: the procfs buffer
|
||||
* @line: the buffer to store
|
||||
* @len: the max. buffer size - 1
|
||||
*
|
||||
* Reads one line from the buffer and stores the string.
|
||||
*
|
||||
* Returns zero if successful, or 1 if error or EOF.
|
||||
*/
|
||||
int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len)
|
||||
{
|
||||
int c = -1;
|
||||
|
||||
if (len <= 0 || buffer->stop || buffer->error)
|
||||
return 1;
|
||||
while (--len > 0) {
|
||||
c = *buffer->curr++;
|
||||
if (c == '\n') {
|
||||
if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
|
||||
buffer->stop = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*line++ = c;
|
||||
if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
|
||||
buffer->stop = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (c != '\n' && !buffer->stop) {
|
||||
c = *buffer->curr++;
|
||||
if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
|
||||
buffer->stop = 1;
|
||||
}
|
||||
}
|
||||
*line = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_get_line - parse a string token
|
||||
* @dest: the buffer to store the string token
|
||||
* @src: the original string
|
||||
* @len: the max. length of token - 1
|
||||
*
|
||||
* Parses the original string and copy a token to the given
|
||||
* string buffer.
|
||||
*
|
||||
* Returns the updated pointer of the original string so that
|
||||
* it can be used for the next call.
|
||||
*/
|
||||
char *snd_info_get_str(char *dest, char *src, int len)
|
||||
{
|
||||
int c;
|
||||
|
||||
while (*src == ' ' || *src == '\t')
|
||||
src++;
|
||||
if (*src == '"' || *src == '\'') {
|
||||
c = *src++;
|
||||
while (--len > 0 && *src && *src != c) {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
if (*src == c)
|
||||
src++;
|
||||
} else {
|
||||
while (--len > 0 && *src && *src != ' ' && *src != '\t') {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
}
|
||||
*dest = 0;
|
||||
while (*src == ' ' || *src == '\t')
|
||||
src++;
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_create_entry - create an info entry
|
||||
* @name: the proc file name
|
||||
*
|
||||
* Creates an info entry with the given file name and initializes as
|
||||
* the default state.
|
||||
*
|
||||
* Usually called from other functions such as
|
||||
* snd_info_create_card_entry().
|
||||
*
|
||||
* Returns the pointer of the new instance, or NULL on failure.
|
||||
*/
|
||||
static snd_info_entry_t *snd_info_create_entry(const char *name)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
entry->name = snd_kmalloc_strdup(name, GFP_KERNEL);
|
||||
if (entry->name == NULL) {
|
||||
kfree(entry);
|
||||
return NULL;
|
||||
}
|
||||
entry->mode = S_IFREG | S_IRUGO;
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
init_MUTEX(&entry->access);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_create_module_entry - create an info entry for the given module
|
||||
* @module: the module pointer
|
||||
* @name: the file name
|
||||
* @parent: the parent directory
|
||||
*
|
||||
* Creates a new info entry and assigns it to the given module.
|
||||
*
|
||||
* Returns the pointer of the new instance, or NULL on failure.
|
||||
*/
|
||||
snd_info_entry_t *snd_info_create_module_entry(struct module * module,
|
||||
const char *name,
|
||||
snd_info_entry_t *parent)
|
||||
{
|
||||
snd_info_entry_t *entry = snd_info_create_entry(name);
|
||||
if (entry) {
|
||||
entry->module = module;
|
||||
entry->parent = parent;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_create_card_entry - create an info entry for the given card
|
||||
* @card: the card instance
|
||||
* @name: the file name
|
||||
* @parent: the parent directory
|
||||
*
|
||||
* Creates a new info entry and assigns it to the given card.
|
||||
*
|
||||
* Returns the pointer of the new instance, or NULL on failure.
|
||||
*/
|
||||
snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
|
||||
const char *name,
|
||||
snd_info_entry_t * parent)
|
||||
{
|
||||
snd_info_entry_t *entry = snd_info_create_entry(name);
|
||||
if (entry) {
|
||||
entry->module = card->module;
|
||||
entry->card = card;
|
||||
entry->parent = parent;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int snd_info_dev_free_entry(snd_device_t *device)
|
||||
{
|
||||
snd_info_entry_t *entry = device->device_data;
|
||||
snd_info_free_entry(entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_info_dev_register_entry(snd_device_t *device)
|
||||
{
|
||||
snd_info_entry_t *entry = device->device_data;
|
||||
return snd_info_register(entry);
|
||||
}
|
||||
|
||||
static int snd_info_dev_disconnect_entry(snd_device_t *device)
|
||||
{
|
||||
snd_info_entry_t *entry = device->device_data;
|
||||
entry->disconnected = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_info_dev_unregister_entry(snd_device_t *device)
|
||||
{
|
||||
snd_info_entry_t *entry = device->device_data;
|
||||
return snd_info_unregister(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_proc_new - create an info entry for the given card
|
||||
* @card: the card instance
|
||||
* @name: the file name
|
||||
* @entryp: the pointer to store the new info entry
|
||||
*
|
||||
* Creates a new info entry and assigns it to the given card.
|
||||
* Unlike snd_info_create_card_entry(), this function registers the
|
||||
* info entry as an ALSA device component, so that it can be
|
||||
* unregistered/released without explicit call.
|
||||
* Also, you don't have to register this entry via snd_info_register(),
|
||||
* since this will be registered by snd_card_register() automatically.
|
||||
*
|
||||
* The parent is assumed as card->proc_root.
|
||||
*
|
||||
* For releasing this entry, use snd_device_free() instead of
|
||||
* snd_info_free_entry().
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_card_proc_new(snd_card_t *card, const char *name,
|
||||
snd_info_entry_t **entryp)
|
||||
{
|
||||
static snd_device_ops_t ops = {
|
||||
.dev_free = snd_info_dev_free_entry,
|
||||
.dev_register = snd_info_dev_register_entry,
|
||||
.dev_disconnect = snd_info_dev_disconnect_entry,
|
||||
.dev_unregister = snd_info_dev_unregister_entry
|
||||
};
|
||||
snd_info_entry_t *entry;
|
||||
int err;
|
||||
|
||||
entry = snd_info_create_card_entry(card, name, card->proc_root);
|
||||
if (! entry)
|
||||
return -ENOMEM;
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return err;
|
||||
}
|
||||
if (entryp)
|
||||
*entryp = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_free_entry - release the info entry
|
||||
* @entry: the info entry
|
||||
*
|
||||
* Releases the info entry. Don't call this after registered.
|
||||
*/
|
||||
void snd_info_free_entry(snd_info_entry_t * entry)
|
||||
{
|
||||
if (entry == NULL)
|
||||
return;
|
||||
kfree(entry->name);
|
||||
if (entry->private_free)
|
||||
entry->private_free(entry);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_register - register the info entry
|
||||
* @entry: the info entry
|
||||
*
|
||||
* Registers the proc info entry.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_info_register(snd_info_entry_t * entry)
|
||||
{
|
||||
struct proc_dir_entry *root, *p = NULL;
|
||||
|
||||
snd_assert(entry != NULL, return -ENXIO);
|
||||
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
|
||||
down(&info_mutex);
|
||||
p = snd_create_proc_entry(entry->name, entry->mode, root);
|
||||
if (!p) {
|
||||
up(&info_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
p->owner = entry->module;
|
||||
if (!S_ISDIR(entry->mode))
|
||||
p->proc_fops = &snd_info_entry_operations;
|
||||
p->size = entry->size;
|
||||
p->data = entry;
|
||||
entry->p = p;
|
||||
up(&info_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_info_unregister - de-register the info entry
|
||||
* @entry: the info entry
|
||||
*
|
||||
* De-registers the info entry and releases the instance.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_info_unregister(snd_info_entry_t * entry)
|
||||
{
|
||||
struct proc_dir_entry *root;
|
||||
|
||||
snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
|
||||
root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
|
||||
snd_assert(root, return -ENXIO);
|
||||
down(&info_mutex);
|
||||
snd_remove_proc_entry(root, entry->p);
|
||||
up(&info_mutex);
|
||||
snd_info_free_entry(entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static snd_info_entry_t *snd_info_version_entry = NULL;
|
||||
|
||||
static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
snd_iprintf(buffer,
|
||||
"Advanced Linux Sound Architecture Driver Version "
|
||||
CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int __init snd_info_version_init(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
|
||||
if (entry == NULL)
|
||||
return -ENOMEM;
|
||||
entry->c.text.read_size = 256;
|
||||
entry->c.text.read = snd_info_version_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_info_version_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit snd_info_version_done(void)
|
||||
{
|
||||
if (snd_info_version_entry)
|
||||
snd_info_unregister(snd_info_version_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
137
sound/core/info_oss.c
Normal file
137
sound/core/info_oss.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Information interface for ALSA driver
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/version.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
|
||||
|
||||
/*
|
||||
* OSS compatible part
|
||||
*/
|
||||
|
||||
static DECLARE_MUTEX(strings);
|
||||
static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
|
||||
static snd_info_entry_t *snd_sndstat_proc_entry;
|
||||
|
||||
int snd_oss_info_register(int dev, int num, char *string)
|
||||
{
|
||||
char *x;
|
||||
|
||||
snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO);
|
||||
snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO);
|
||||
down(&strings);
|
||||
if (string == NULL) {
|
||||
if ((x = snd_sndstat_strings[num][dev]) != NULL) {
|
||||
kfree(x);
|
||||
x = NULL;
|
||||
}
|
||||
} else {
|
||||
x = snd_kmalloc_strdup(string, GFP_KERNEL);
|
||||
if (x == NULL) {
|
||||
up(&strings);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
snd_sndstat_strings[num][dev] = x;
|
||||
up(&strings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void snd_card_info_read_oss(snd_info_buffer_t * buffer);
|
||||
|
||||
static int snd_sndstat_show_strings(snd_info_buffer_t * buf, char *id, int dev)
|
||||
{
|
||||
int idx, ok = -1;
|
||||
char *str;
|
||||
|
||||
snd_iprintf(buf, "\n%s:", id);
|
||||
down(&strings);
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
str = snd_sndstat_strings[idx][dev];
|
||||
if (str) {
|
||||
if (ok < 0) {
|
||||
snd_iprintf(buf, "\n");
|
||||
ok++;
|
||||
}
|
||||
snd_iprintf(buf, "%i: %s\n", idx, str);
|
||||
}
|
||||
}
|
||||
up(&strings);
|
||||
if (ok < 0)
|
||||
snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void snd_sndstat_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
|
||||
snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
|
||||
system_utsname.sysname,
|
||||
system_utsname.nodename,
|
||||
system_utsname.release,
|
||||
system_utsname.version,
|
||||
system_utsname.machine);
|
||||
snd_iprintf(buffer, "Config options: 0\n");
|
||||
snd_iprintf(buffer, "\nInstalled drivers: \n");
|
||||
snd_iprintf(buffer, "Type 10: ALSA emulation\n");
|
||||
snd_iprintf(buffer, "\nCard config: \n");
|
||||
snd_card_info_read_oss(buffer);
|
||||
snd_sndstat_show_strings(buffer, "Audio devices", SNDRV_OSS_INFO_DEV_AUDIO);
|
||||
snd_sndstat_show_strings(buffer, "Synth devices", SNDRV_OSS_INFO_DEV_SYNTH);
|
||||
snd_sndstat_show_strings(buffer, "Midi devices", SNDRV_OSS_INFO_DEV_MIDI);
|
||||
snd_sndstat_show_strings(buffer, "Timers", SNDRV_OSS_INFO_DEV_TIMERS);
|
||||
snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
|
||||
}
|
||||
|
||||
int snd_info_minor_register(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
|
||||
if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
|
||||
entry->c.text.read_size = 2048;
|
||||
entry->c.text.read = snd_sndstat_proc_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
snd_sndstat_proc_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_info_minor_unregister(void)
|
||||
{
|
||||
if (snd_sndstat_proc_entry) {
|
||||
snd_info_unregister(snd_sndstat_proc_entry);
|
||||
snd_sndstat_proc_entry = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SND_OSSEMUL */
|
||||
887
sound/core/init.c
Normal file
887
sound/core/init.c
Normal file
@@ -0,0 +1,887 @@
|
||||
/*
|
||||
* Initialization routines
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
struct snd_shutdown_f_ops {
|
||||
struct file_operations f_ops;
|
||||
struct snd_shutdown_f_ops *next;
|
||||
};
|
||||
|
||||
unsigned int snd_cards_lock = 0; /* locked for registering/using */
|
||||
snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
|
||||
DEFINE_RWLOCK(snd_card_rwlock);
|
||||
|
||||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag);
|
||||
#endif
|
||||
|
||||
static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
snd_iprintf(buffer, "%s\n", entry->card->id);
|
||||
}
|
||||
|
||||
static void snd_card_free_thread(void * __card);
|
||||
|
||||
/**
|
||||
* snd_card_new - create and initialize a soundcard structure
|
||||
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
|
||||
* @xid: card identification (ASCII string)
|
||||
* @module: top level module for locking
|
||||
* @extra_size: allocate this extra size after the main soundcard structure
|
||||
*
|
||||
* Creates and initializes a soundcard structure.
|
||||
*
|
||||
* Returns kmallocated snd_card_t structure. Creates the ALSA control interface
|
||||
* (which is blocked until snd_card_register function is called).
|
||||
*/
|
||||
snd_card_t *snd_card_new(int idx, const char *xid,
|
||||
struct module *module, int extra_size)
|
||||
{
|
||||
snd_card_t *card;
|
||||
int err;
|
||||
|
||||
if (extra_size < 0)
|
||||
extra_size = 0;
|
||||
card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL);
|
||||
if (card == NULL)
|
||||
return NULL;
|
||||
if (xid) {
|
||||
if (!snd_info_check_reserved_words(xid))
|
||||
goto __error;
|
||||
strlcpy(card->id, xid, sizeof(card->id));
|
||||
}
|
||||
err = 0;
|
||||
write_lock(&snd_card_rwlock);
|
||||
if (idx < 0) {
|
||||
int idx2;
|
||||
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
|
||||
if (~snd_cards_lock & idx & 1<<idx2) {
|
||||
idx = idx2;
|
||||
if (idx >= snd_ecards_limit)
|
||||
snd_ecards_limit = idx + 1;
|
||||
break;
|
||||
}
|
||||
} else if (idx < snd_ecards_limit) {
|
||||
if (snd_cards_lock & (1 << idx))
|
||||
err = -ENODEV; /* invalid */
|
||||
} else if (idx < SNDRV_CARDS)
|
||||
snd_ecards_limit = idx + 1; /* increase the limit */
|
||||
else
|
||||
err = -ENODEV;
|
||||
if (idx < 0 || err < 0) {
|
||||
write_unlock(&snd_card_rwlock);
|
||||
snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
|
||||
goto __error;
|
||||
}
|
||||
snd_cards_lock |= 1 << idx; /* lock it */
|
||||
write_unlock(&snd_card_rwlock);
|
||||
card->number = idx;
|
||||
card->module = module;
|
||||
INIT_LIST_HEAD(&card->devices);
|
||||
init_rwsem(&card->controls_rwsem);
|
||||
rwlock_init(&card->ctl_files_rwlock);
|
||||
INIT_LIST_HEAD(&card->controls);
|
||||
INIT_LIST_HEAD(&card->ctl_files);
|
||||
spin_lock_init(&card->files_lock);
|
||||
init_waitqueue_head(&card->shutdown_sleep);
|
||||
INIT_WORK(&card->free_workq, snd_card_free_thread, card);
|
||||
#ifdef CONFIG_PM
|
||||
init_MUTEX(&card->power_lock);
|
||||
init_waitqueue_head(&card->power_sleep);
|
||||
#endif
|
||||
/* the control interface cannot be accessed from the user space until */
|
||||
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
|
||||
if ((err = snd_ctl_create(card)) < 0) {
|
||||
snd_printd("unable to register control minors\n");
|
||||
goto __error;
|
||||
}
|
||||
if ((err = snd_info_card_create(card)) < 0) {
|
||||
snd_printd("unable to create card info\n");
|
||||
goto __error_ctl;
|
||||
}
|
||||
if (extra_size > 0)
|
||||
card->private_data = (char *)card + sizeof(snd_card_t);
|
||||
return card;
|
||||
|
||||
__error_ctl:
|
||||
snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
|
||||
__error:
|
||||
kfree(card);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
|
||||
{
|
||||
return POLLERR | POLLNVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_disconnect - disconnect all APIs from the file-operations (user space)
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* Disconnects all APIs from the file-operations (user space).
|
||||
*
|
||||
* Returns zero, otherwise a negative error code.
|
||||
*
|
||||
* Note: The current implementation replaces all active file->f_op with special
|
||||
* dummy file operations (they do nothing except release).
|
||||
*/
|
||||
int snd_card_disconnect(snd_card_t * card)
|
||||
{
|
||||
struct snd_monitor_file *mfile;
|
||||
struct file *file;
|
||||
struct snd_shutdown_f_ops *s_f_ops;
|
||||
struct file_operations *f_ops, *old_f_ops;
|
||||
int err;
|
||||
|
||||
spin_lock(&card->files_lock);
|
||||
if (card->shutdown) {
|
||||
spin_unlock(&card->files_lock);
|
||||
return 0;
|
||||
}
|
||||
card->shutdown = 1;
|
||||
spin_unlock(&card->files_lock);
|
||||
|
||||
/* phase 1: disable fops (user space) operations for ALSA API */
|
||||
write_lock(&snd_card_rwlock);
|
||||
snd_cards[card->number] = NULL;
|
||||
write_unlock(&snd_card_rwlock);
|
||||
|
||||
/* phase 2: replace file->f_op with special dummy operations */
|
||||
|
||||
spin_lock(&card->files_lock);
|
||||
mfile = card->files;
|
||||
while (mfile) {
|
||||
file = mfile->file;
|
||||
|
||||
/* it's critical part, use endless loop */
|
||||
/* we have no room to fail */
|
||||
s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC);
|
||||
if (s_f_ops == NULL)
|
||||
panic("Atomic allocation failed for snd_shutdown_f_ops!");
|
||||
|
||||
f_ops = &s_f_ops->f_ops;
|
||||
|
||||
memset(f_ops, 0, sizeof(*f_ops));
|
||||
f_ops->owner = file->f_op->owner;
|
||||
f_ops->release = file->f_op->release;
|
||||
f_ops->poll = snd_disconnect_poll;
|
||||
|
||||
s_f_ops->next = card->s_f_ops;
|
||||
card->s_f_ops = s_f_ops;
|
||||
|
||||
f_ops = fops_get(f_ops);
|
||||
|
||||
old_f_ops = file->f_op;
|
||||
file->f_op = f_ops; /* must be atomic */
|
||||
fops_put(old_f_ops);
|
||||
|
||||
mfile = mfile->next;
|
||||
}
|
||||
spin_unlock(&card->files_lock);
|
||||
|
||||
/* phase 3: notify all connected devices about disconnection */
|
||||
/* at this point, they cannot respond to any calls except release() */
|
||||
|
||||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
if (snd_mixer_oss_notify_callback)
|
||||
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
|
||||
#endif
|
||||
|
||||
/* notify all devices that we are disconnected */
|
||||
err = snd_device_disconnect_all(card);
|
||||
if (err < 0)
|
||||
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_SND_GENERIC_PM)
|
||||
static void snd_generic_device_unregister(struct snd_generic_device *dev);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* snd_card_free - frees given soundcard structure
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* This function releases the soundcard structure and the all assigned
|
||||
* devices automatically. That is, you don't have to release the devices
|
||||
* by yourself.
|
||||
*
|
||||
* Returns zero. Frees all associated devices and frees the control
|
||||
* interface associated to given soundcard.
|
||||
*/
|
||||
int snd_card_free(snd_card_t * card)
|
||||
{
|
||||
struct snd_shutdown_f_ops *s_f_ops;
|
||||
|
||||
if (card == NULL)
|
||||
return -EINVAL;
|
||||
write_lock(&snd_card_rwlock);
|
||||
snd_cards[card->number] = NULL;
|
||||
write_unlock(&snd_card_rwlock);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
wake_up(&card->power_sleep);
|
||||
#ifdef CONFIG_SND_GENERIC_PM
|
||||
if (card->pm_dev) {
|
||||
snd_generic_device_unregister(card->pm_dev);
|
||||
card->pm_dev = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* wait, until all devices are ready for the free operation */
|
||||
wait_event(card->shutdown_sleep, card->files == NULL);
|
||||
|
||||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
if (snd_mixer_oss_notify_callback)
|
||||
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
|
||||
#endif
|
||||
if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
|
||||
snd_printk(KERN_ERR "unable to free all devices (pre)\n");
|
||||
/* Fatal, but this situation should never occur */
|
||||
}
|
||||
if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
|
||||
snd_printk(KERN_ERR "unable to free all devices (normal)\n");
|
||||
/* Fatal, but this situation should never occur */
|
||||
}
|
||||
if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
|
||||
snd_printk(KERN_ERR "unable to free all devices (post)\n");
|
||||
/* Fatal, but this situation should never occur */
|
||||
}
|
||||
if (card->private_free)
|
||||
card->private_free(card);
|
||||
if (card->proc_id)
|
||||
snd_info_unregister(card->proc_id);
|
||||
if (snd_info_card_free(card) < 0) {
|
||||
snd_printk(KERN_WARNING "unable to free card info\n");
|
||||
/* Not fatal error */
|
||||
}
|
||||
while (card->s_f_ops) {
|
||||
s_f_ops = card->s_f_ops;
|
||||
card->s_f_ops = s_f_ops->next;
|
||||
kfree(s_f_ops);
|
||||
}
|
||||
write_lock(&snd_card_rwlock);
|
||||
snd_cards_lock &= ~(1 << card->number);
|
||||
write_unlock(&snd_card_rwlock);
|
||||
kfree(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_card_free_thread(void * __card)
|
||||
{
|
||||
snd_card_t *card = __card;
|
||||
struct module * module = card->module;
|
||||
|
||||
if (!try_module_get(module)) {
|
||||
snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
|
||||
module = NULL;
|
||||
}
|
||||
|
||||
snd_card_free(card);
|
||||
|
||||
module_put(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_free_in_thread - call snd_card_free() in thread
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* This function schedules the call of snd_card_free() function in a
|
||||
* work queue. When all devices are released (non-busy), the work
|
||||
* is woken up and calls snd_card_free().
|
||||
*
|
||||
* When a card can be disconnected at any time by hotplug service,
|
||||
* this function should be used in disconnect (or detach) callback
|
||||
* instead of calling snd_card_free() directly.
|
||||
*
|
||||
* Returns - zero otherwise a negative error code if the start of thread failed.
|
||||
*/
|
||||
int snd_card_free_in_thread(snd_card_t * card)
|
||||
{
|
||||
if (card->files == NULL) {
|
||||
snd_card_free(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (schedule_work(&card->free_workq))
|
||||
return 0;
|
||||
|
||||
snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
|
||||
/* try to free the structure immediately */
|
||||
snd_card_free(card);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static void choose_default_id(snd_card_t * card)
|
||||
{
|
||||
int i, len, idx_flag = 0, loops = 8;
|
||||
char *id, *spos;
|
||||
|
||||
id = spos = card->shortname;
|
||||
while (*id != '\0') {
|
||||
if (*id == ' ')
|
||||
spos = id + 1;
|
||||
id++;
|
||||
}
|
||||
id = card->id;
|
||||
while (*spos != '\0' && !isalnum(*spos))
|
||||
spos++;
|
||||
if (isdigit(*spos))
|
||||
*id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
|
||||
while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
|
||||
if (isalnum(*spos))
|
||||
*id++ = *spos;
|
||||
spos++;
|
||||
}
|
||||
*id = '\0';
|
||||
|
||||
id = card->id;
|
||||
|
||||
if (*id == '\0')
|
||||
strcpy(id, "default");
|
||||
|
||||
while (1) {
|
||||
if (loops-- == 0) {
|
||||
snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
|
||||
strcpy(card->id, card->proc_root->name);
|
||||
return;
|
||||
}
|
||||
if (!snd_info_check_reserved_words(id))
|
||||
goto __change;
|
||||
for (i = 0; i < snd_ecards_limit; i++) {
|
||||
if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
|
||||
goto __change;
|
||||
}
|
||||
break;
|
||||
|
||||
__change:
|
||||
len = strlen(id);
|
||||
if (idx_flag)
|
||||
id[len-1]++;
|
||||
else if ((size_t)len <= sizeof(card->id) - 3) {
|
||||
strcat(id, "_1");
|
||||
idx_flag++;
|
||||
} else {
|
||||
spos = id + len - 2;
|
||||
if ((size_t)len <= sizeof(card->id) - 2)
|
||||
spos++;
|
||||
*spos++ = '_';
|
||||
*spos++ = '1';
|
||||
*spos++ = '\0';
|
||||
idx_flag++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_register - register the soundcard
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* This function registers all the devices assigned to the soundcard.
|
||||
* Until calling this, the ALSA control interface is blocked from the
|
||||
* external accesses. Thus, you should call this function at the end
|
||||
* of the initialization of the card.
|
||||
*
|
||||
* Returns zero otherwise a negative error code if the registrain failed.
|
||||
*/
|
||||
int snd_card_register(snd_card_t * card)
|
||||
{
|
||||
int err;
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
snd_runtime_check(card != NULL, return -EINVAL);
|
||||
if ((err = snd_device_register_all(card)) < 0)
|
||||
return err;
|
||||
write_lock(&snd_card_rwlock);
|
||||
if (snd_cards[card->number]) {
|
||||
/* already registered */
|
||||
write_unlock(&snd_card_rwlock);
|
||||
return 0;
|
||||
}
|
||||
if (card->id[0] == '\0')
|
||||
choose_default_id(card);
|
||||
snd_cards[card->number] = card;
|
||||
write_unlock(&snd_card_rwlock);
|
||||
if ((err = snd_info_card_register(card)) < 0) {
|
||||
snd_printd("unable to create card info\n");
|
||||
goto __skip_info;
|
||||
}
|
||||
if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
|
||||
snd_printd("unable to create card entry\n");
|
||||
goto __skip_info;
|
||||
}
|
||||
entry->c.text.read_size = PAGE_SIZE;
|
||||
entry->c.text.read = snd_card_id_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
card->proc_id = entry;
|
||||
__skip_info:
|
||||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
if (snd_mixer_oss_notify_callback)
|
||||
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_info_entry_t *snd_card_info_entry = NULL;
|
||||
|
||||
static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
int idx, count;
|
||||
snd_card_t *card;
|
||||
|
||||
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
|
||||
read_lock(&snd_card_rwlock);
|
||||
if ((card = snd_cards[idx]) != NULL) {
|
||||
count++;
|
||||
snd_iprintf(buffer, "%i [%-15s]: %s - %s\n",
|
||||
idx,
|
||||
card->id,
|
||||
card->driver,
|
||||
card->shortname);
|
||||
snd_iprintf(buffer, " %s\n",
|
||||
card->longname);
|
||||
}
|
||||
read_unlock(&snd_card_rwlock);
|
||||
}
|
||||
if (!count)
|
||||
snd_iprintf(buffer, "--- no soundcards ---\n");
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
|
||||
|
||||
void snd_card_info_read_oss(snd_info_buffer_t * buffer)
|
||||
{
|
||||
int idx, count;
|
||||
snd_card_t *card;
|
||||
|
||||
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
|
||||
read_lock(&snd_card_rwlock);
|
||||
if ((card = snd_cards[idx]) != NULL) {
|
||||
count++;
|
||||
snd_iprintf(buffer, "%s\n", card->longname);
|
||||
}
|
||||
read_unlock(&snd_card_rwlock);
|
||||
}
|
||||
if (!count) {
|
||||
snd_iprintf(buffer, "--- no soundcards ---\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
static snd_info_entry_t *snd_card_module_info_entry;
|
||||
static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
int idx;
|
||||
snd_card_t *card;
|
||||
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
read_lock(&snd_card_rwlock);
|
||||
if ((card = snd_cards[idx]) != NULL)
|
||||
snd_iprintf(buffer, "%i %s\n", idx, card->module->name);
|
||||
read_unlock(&snd_card_rwlock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int __init snd_card_info_init(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
|
||||
snd_runtime_check(entry != NULL, return -ENOMEM);
|
||||
entry->c.text.read_size = PAGE_SIZE;
|
||||
entry->c.text.read = snd_card_info_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_card_info_entry = entry;
|
||||
|
||||
#ifdef MODULE
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
|
||||
if (entry) {
|
||||
entry->c.text.read_size = PAGE_SIZE;
|
||||
entry->c.text.read = snd_card_module_info_read;
|
||||
if (snd_info_register(entry) < 0)
|
||||
snd_info_free_entry(entry);
|
||||
else
|
||||
snd_card_module_info_entry = entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_card_info_done(void)
|
||||
{
|
||||
if (snd_card_info_entry)
|
||||
snd_info_unregister(snd_card_info_entry);
|
||||
#ifdef MODULE
|
||||
if (snd_card_module_info_entry)
|
||||
snd_info_unregister(snd_card_module_info_entry);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_component_add - add a component string
|
||||
* @card: soundcard structure
|
||||
* @component: the component id string
|
||||
*
|
||||
* This function adds the component id string to the supported list.
|
||||
* The component can be referred from the alsa-lib.
|
||||
*
|
||||
* Returns zero otherwise a negative error code.
|
||||
*/
|
||||
|
||||
int snd_component_add(snd_card_t *card, const char *component)
|
||||
{
|
||||
char *ptr;
|
||||
int len = strlen(component);
|
||||
|
||||
ptr = strstr(card->components, component);
|
||||
if (ptr != NULL) {
|
||||
if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */
|
||||
return 1;
|
||||
}
|
||||
if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
|
||||
snd_BUG();
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (card->components[0] != '\0')
|
||||
strcat(card->components, " ");
|
||||
strcat(card->components, component);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_file_add - add the file to the file list of the card
|
||||
* @card: soundcard structure
|
||||
* @file: file pointer
|
||||
*
|
||||
* This function adds the file to the file linked-list of the card.
|
||||
* This linked-list is used to keep tracking the connection state,
|
||||
* and to avoid the release of busy resources by hotplug.
|
||||
*
|
||||
* Returns zero or a negative error code.
|
||||
*/
|
||||
int snd_card_file_add(snd_card_t *card, struct file *file)
|
||||
{
|
||||
struct snd_monitor_file *mfile;
|
||||
|
||||
mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
|
||||
if (mfile == NULL)
|
||||
return -ENOMEM;
|
||||
mfile->file = file;
|
||||
mfile->next = NULL;
|
||||
spin_lock(&card->files_lock);
|
||||
if (card->shutdown) {
|
||||
spin_unlock(&card->files_lock);
|
||||
kfree(mfile);
|
||||
return -ENODEV;
|
||||
}
|
||||
mfile->next = card->files;
|
||||
card->files = mfile;
|
||||
spin_unlock(&card->files_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_file_remove - remove the file from the file list
|
||||
* @card: soundcard structure
|
||||
* @file: file pointer
|
||||
*
|
||||
* This function removes the file formerly added to the card via
|
||||
* snd_card_file_add() function.
|
||||
* If all files are removed and the release of the card is
|
||||
* scheduled, it will wake up the the thread to call snd_card_free()
|
||||
* (see snd_card_free_in_thread() function).
|
||||
*
|
||||
* Returns zero or a negative error code.
|
||||
*/
|
||||
int snd_card_file_remove(snd_card_t *card, struct file *file)
|
||||
{
|
||||
struct snd_monitor_file *mfile, *pfile = NULL;
|
||||
|
||||
spin_lock(&card->files_lock);
|
||||
mfile = card->files;
|
||||
while (mfile) {
|
||||
if (mfile->file == file) {
|
||||
if (pfile)
|
||||
pfile->next = mfile->next;
|
||||
else
|
||||
card->files = mfile->next;
|
||||
break;
|
||||
}
|
||||
pfile = mfile;
|
||||
mfile = mfile->next;
|
||||
}
|
||||
spin_unlock(&card->files_lock);
|
||||
if (card->files == NULL)
|
||||
wake_up(&card->shutdown_sleep);
|
||||
if (!mfile) {
|
||||
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
|
||||
return -ENOENT;
|
||||
}
|
||||
kfree(mfile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* snd_power_wait - wait until the power-state is changed.
|
||||
* @card: soundcard structure
|
||||
* @power_state: expected power state
|
||||
* @file: file structure for the O_NONBLOCK check (optional)
|
||||
*
|
||||
* Waits until the power-state is changed.
|
||||
*
|
||||
* Note: the power lock must be active before call.
|
||||
*/
|
||||
int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file)
|
||||
{
|
||||
wait_queue_t wait;
|
||||
int result = 0;
|
||||
|
||||
/* fastpath */
|
||||
if (snd_power_get_state(card) == power_state)
|
||||
return 0;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&card->power_sleep, &wait);
|
||||
while (1) {
|
||||
if (card->shutdown) {
|
||||
result = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (snd_power_get_state(card) == power_state)
|
||||
break;
|
||||
#if 0 /* block all devices */
|
||||
if (file && (file->f_flags & O_NONBLOCK)) {
|
||||
result = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
snd_power_unlock(card);
|
||||
schedule_timeout(30 * HZ);
|
||||
snd_power_lock(card);
|
||||
}
|
||||
remove_wait_queue(&card->power_sleep, &wait);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_set_pm_callback - set the PCI power-management callbacks
|
||||
* @card: soundcard structure
|
||||
* @suspend: suspend callback function
|
||||
* @resume: resume callback function
|
||||
* @private_data: private data to pass to the callback functions
|
||||
*
|
||||
* Sets the power-management callback functions of the card.
|
||||
* These callbacks are called from ALSA's common PCI suspend/resume
|
||||
* handler and from the control API.
|
||||
*/
|
||||
int snd_card_set_pm_callback(snd_card_t *card,
|
||||
int (*suspend)(snd_card_t *, pm_message_t),
|
||||
int (*resume)(snd_card_t *),
|
||||
void *private_data)
|
||||
{
|
||||
card->pm_suspend = suspend;
|
||||
card->pm_resume = resume;
|
||||
card->pm_private_data = private_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_GENERIC_PM
|
||||
/*
|
||||
* use platform_device for generic power-management without a proper bus
|
||||
* (e.g. ISA)
|
||||
*/
|
||||
struct snd_generic_device {
|
||||
struct platform_device pdev;
|
||||
snd_card_t *card;
|
||||
};
|
||||
|
||||
#define get_snd_generic_card(dev) container_of(to_platform_device(dev), struct snd_generic_device, pdev)->card
|
||||
|
||||
#define SND_GENERIC_NAME "snd_generic_pm"
|
||||
|
||||
static int snd_generic_suspend(struct device *dev, u32 state, u32 level);
|
||||
static int snd_generic_resume(struct device *dev, u32 level);
|
||||
|
||||
static struct device_driver snd_generic_driver = {
|
||||
.name = SND_GENERIC_NAME,
|
||||
.bus = &platform_bus_type,
|
||||
.suspend = snd_generic_suspend,
|
||||
.resume = snd_generic_resume,
|
||||
};
|
||||
|
||||
static int generic_driver_registered;
|
||||
|
||||
static void generic_driver_unregister(void)
|
||||
{
|
||||
if (generic_driver_registered) {
|
||||
generic_driver_registered--;
|
||||
if (! generic_driver_registered)
|
||||
driver_unregister(&snd_generic_driver);
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_generic_device *snd_generic_device_register(snd_card_t *card)
|
||||
{
|
||||
struct snd_generic_device *dev;
|
||||
|
||||
if (! generic_driver_registered) {
|
||||
if (driver_register(&snd_generic_driver) < 0)
|
||||
return NULL;
|
||||
}
|
||||
generic_driver_registered++;
|
||||
|
||||
dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
|
||||
if (! dev) {
|
||||
generic_driver_unregister();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->pdev.name = SND_GENERIC_NAME;
|
||||
dev->pdev.id = card->number;
|
||||
dev->card = card;
|
||||
if (platform_device_register(&dev->pdev) < 0) {
|
||||
kfree(dev);
|
||||
generic_driver_unregister();
|
||||
return NULL;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void snd_generic_device_unregister(struct snd_generic_device *dev)
|
||||
{
|
||||
platform_device_unregister(&dev->pdev);
|
||||
kfree(dev);
|
||||
generic_driver_unregister();
|
||||
}
|
||||
|
||||
/* suspend/resume callbacks for snd_generic platform device */
|
||||
static int snd_generic_suspend(struct device *dev, u32 state, u32 level)
|
||||
{
|
||||
snd_card_t *card;
|
||||
|
||||
if (level != SUSPEND_DISABLE)
|
||||
return 0;
|
||||
|
||||
card = get_snd_generic_card(dev);
|
||||
if (card->power_state == SNDRV_CTL_POWER_D3hot)
|
||||
return 0;
|
||||
card->pm_suspend(card, PMSG_SUSPEND);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_generic_resume(struct device *dev, u32 level)
|
||||
{
|
||||
snd_card_t *card;
|
||||
|
||||
if (level != RESUME_ENABLE)
|
||||
return 0;
|
||||
|
||||
card = get_snd_generic_card(dev);
|
||||
if (card->power_state == SNDRV_CTL_POWER_D0)
|
||||
return 0;
|
||||
card->pm_resume(card);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_set_generic_pm_callback - set the generic power-management callbacks
|
||||
* @card: soundcard structure
|
||||
* @suspend: suspend callback function
|
||||
* @resume: resume callback function
|
||||
* @private_data: private data to pass to the callback functions
|
||||
*
|
||||
* Registers the power-management and sets the lowlevel callbacks for
|
||||
* the given card. These callbacks are called from the ALSA's common
|
||||
* PM handler and from the control API.
|
||||
*/
|
||||
int snd_card_set_generic_pm_callback(snd_card_t *card,
|
||||
int (*suspend)(snd_card_t *, pm_message_t),
|
||||
int (*resume)(snd_card_t *),
|
||||
void *private_data)
|
||||
{
|
||||
card->pm_dev = snd_generic_device_register(card);
|
||||
if (! card->pm_dev)
|
||||
return -ENOMEM;
|
||||
snd_card_set_pm_callback(card, suspend, resume, private_data);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SND_GENERIC_PM */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
int snd_card_pci_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
snd_card_t *card = pci_get_drvdata(dev);
|
||||
int err;
|
||||
if (! card || ! card->pm_suspend)
|
||||
return 0;
|
||||
if (card->power_state == SNDRV_CTL_POWER_D3hot)
|
||||
return 0;
|
||||
err = card->pm_suspend(card, PMSG_SUSPEND);
|
||||
pci_save_state(dev);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_card_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
snd_card_t *card = pci_get_drvdata(dev);
|
||||
if (! card || ! card->pm_resume)
|
||||
return 0;
|
||||
if (card->power_state == SNDRV_CTL_POWER_D0)
|
||||
return 0;
|
||||
/* restore the PCI config space */
|
||||
pci_restore_state(dev);
|
||||
card->pm_resume(card);
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
103
sound/core/isadma.c
Normal file
103
sound/core/isadma.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* ISA DMA support functions
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Defining following add some delay. Maybe this helps for some broken
|
||||
* ISA DMA controllers.
|
||||
*/
|
||||
|
||||
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <asm/dma.h>
|
||||
|
||||
/**
|
||||
* snd_dma_program - program an ISA DMA transfer
|
||||
* @dma: the dma number
|
||||
* @addr: the physical address of the buffer
|
||||
* @size: the DMA transfer size
|
||||
* @mode: the DMA transfer mode, DMA_MODE_XXX
|
||||
*
|
||||
* Programs an ISA DMA transfer for the given buffer.
|
||||
*/
|
||||
void snd_dma_program(unsigned long dma,
|
||||
unsigned long addr, unsigned int size,
|
||||
unsigned short mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = claim_dma_lock();
|
||||
disable_dma(dma);
|
||||
clear_dma_ff(dma);
|
||||
set_dma_mode(dma, mode);
|
||||
set_dma_addr(dma, addr);
|
||||
set_dma_count(dma, size);
|
||||
if (!(mode & DMA_MODE_NO_ENABLE))
|
||||
enable_dma(dma);
|
||||
release_dma_lock(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_dma_disable - stop the ISA DMA transfer
|
||||
* @dma: the dma number
|
||||
*
|
||||
* Stops the ISA DMA transfer.
|
||||
*/
|
||||
void snd_dma_disable(unsigned long dma)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = claim_dma_lock();
|
||||
clear_dma_ff(dma);
|
||||
disable_dma(dma);
|
||||
release_dma_lock(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes
|
||||
* @dma: the dma number
|
||||
* @size: the dma transfer size
|
||||
*
|
||||
* Returns the current pointer in DMA tranfer buffer in bytes
|
||||
*/
|
||||
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int result;
|
||||
|
||||
flags = claim_dma_lock();
|
||||
clear_dma_ff(dma);
|
||||
if (!isa_dma_bridge_buggy)
|
||||
disable_dma(dma);
|
||||
result = get_dma_residue(dma);
|
||||
if (!isa_dma_bridge_buggy)
|
||||
enable_dma(dma);
|
||||
release_dma_lock(flags);
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (result > size)
|
||||
snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
|
||||
#endif
|
||||
if (result >= size || result == 0)
|
||||
return 0;
|
||||
else
|
||||
return size - result;
|
||||
}
|
||||
663
sound/core/memalloc.c
Normal file
663
sound/core/memalloc.c
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
* Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* Generic memory allocators
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <sound/memalloc.h>
|
||||
#ifdef CONFIG_SBUS
|
||||
#include <asm/sbus.h>
|
||||
#endif
|
||||
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Memory allocator for ALSA system.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
#ifndef SNDRV_CARDS
|
||||
#define SNDRV_CARDS 8
|
||||
#endif
|
||||
|
||||
/* FIXME: so far only some PCI devices have the preallocation table */
|
||||
#ifdef CONFIG_PCI
|
||||
static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
|
||||
#endif
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
size_t size, struct snd_dma_buffer *dmab,
|
||||
size_t *res_size);
|
||||
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
static DECLARE_MUTEX(list_mutex);
|
||||
static LIST_HEAD(mem_list_head);
|
||||
|
||||
/* buffer preservation list */
|
||||
struct snd_mem_list {
|
||||
struct snd_dma_buffer buffer;
|
||||
unsigned int id;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* id for pre-allocated buffers */
|
||||
#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
#define __ASTRING__(x) #x
|
||||
#define snd_assert(expr, args...) do {\
|
||||
if (!(expr)) {\
|
||||
printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
|
||||
args;\
|
||||
}\
|
||||
} while (0)
|
||||
#else
|
||||
#define snd_assert(expr, args...) /**/
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hacks
|
||||
*/
|
||||
|
||||
#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
|
||||
/*
|
||||
* A hack to allocate large buffers via dma_alloc_coherent()
|
||||
*
|
||||
* since dma_alloc_coherent always tries GFP_DMA when the requested
|
||||
* pci memory region is below 32bit, it happens quite often that even
|
||||
* 2 order of pages cannot be allocated.
|
||||
*
|
||||
* so in the following, we allocate at first without dma_mask, so that
|
||||
* allocation will be done without GFP_DMA. if the area doesn't match
|
||||
* with the requested region, then realloate with the original dma_mask
|
||||
* again.
|
||||
*
|
||||
* Really, we want to move this type of thing into dma_alloc_coherent()
|
||||
* so dma_mask doesn't have to be messed with.
|
||||
*/
|
||||
|
||||
static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, int flags)
|
||||
{
|
||||
void *ret;
|
||||
u64 dma_mask, coherent_dma_mask;
|
||||
|
||||
if (dev == NULL || !dev->dma_mask)
|
||||
return dma_alloc_coherent(dev, size, dma_handle, flags);
|
||||
dma_mask = *dev->dma_mask;
|
||||
coherent_dma_mask = dev->coherent_dma_mask;
|
||||
*dev->dma_mask = 0xffffffff; /* do without masking */
|
||||
dev->coherent_dma_mask = 0xffffffff; /* do without masking */
|
||||
ret = dma_alloc_coherent(dev, size, dma_handle, flags);
|
||||
*dev->dma_mask = dma_mask; /* restore */
|
||||
dev->coherent_dma_mask = coherent_dma_mask; /* restore */
|
||||
if (ret) {
|
||||
/* obtained address is out of range? */
|
||||
if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) {
|
||||
/* reallocate with the proper mask */
|
||||
dma_free_coherent(dev, size, ret, *dma_handle);
|
||||
ret = dma_alloc_coherent(dev, size, dma_handle, flags);
|
||||
}
|
||||
} else {
|
||||
/* wish to success now with the proper mask... */
|
||||
if (dma_mask != 0xffffffffUL) {
|
||||
/* allocation with GFP_ATOMIC to avoid the long stall */
|
||||
flags &= ~GFP_KERNEL;
|
||||
flags |= GFP_ATOMIC;
|
||||
ret = dma_alloc_coherent(dev, size, dma_handle, flags);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* redefine dma_alloc_coherent for some architectures */
|
||||
#undef dma_alloc_coherent
|
||||
#define dma_alloc_coherent snd_dma_hack_alloc_coherent
|
||||
|
||||
#endif /* arch */
|
||||
|
||||
#if ! defined(__arm__)
|
||||
#define NEED_RESERVE_PAGES
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
* Generic memory allocators
|
||||
*
|
||||
*/
|
||||
|
||||
static long snd_allocated_pages; /* holding the number of allocated pages */
|
||||
|
||||
static inline void inc_snd_pages(int order)
|
||||
{
|
||||
snd_allocated_pages += 1 << order;
|
||||
}
|
||||
|
||||
static inline void dec_snd_pages(int order)
|
||||
{
|
||||
snd_allocated_pages -= 1 << order;
|
||||
}
|
||||
|
||||
static void mark_pages(struct page *page, int order)
|
||||
{
|
||||
struct page *last_page = page + (1 << order);
|
||||
while (page < last_page)
|
||||
SetPageReserved(page++);
|
||||
}
|
||||
|
||||
static void unmark_pages(struct page *page, int order)
|
||||
{
|
||||
struct page *last_page = page + (1 << order);
|
||||
while (page < last_page)
|
||||
ClearPageReserved(page++);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_malloc_pages - allocate pages with the given size
|
||||
* @size: the size to allocate in bytes
|
||||
* @gfp_flags: the allocation conditions, GFP_XXX
|
||||
*
|
||||
* Allocates the physically contiguous pages with the given size.
|
||||
*
|
||||
* Returns the pointer of the buffer, or NULL if no enoguh memory.
|
||||
*/
|
||||
void *snd_malloc_pages(size_t size, unsigned int gfp_flags)
|
||||
{
|
||||
int pg;
|
||||
void *res;
|
||||
|
||||
snd_assert(size > 0, return NULL);
|
||||
snd_assert(gfp_flags != 0, return NULL);
|
||||
pg = get_order(size);
|
||||
if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) {
|
||||
mark_pages(virt_to_page(res), pg);
|
||||
inc_snd_pages(pg);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_free_pages - release the pages
|
||||
* @ptr: the buffer pointer to release
|
||||
* @size: the allocated buffer size
|
||||
*
|
||||
* Releases the buffer allocated via snd_malloc_pages().
|
||||
*/
|
||||
void snd_free_pages(void *ptr, size_t size)
|
||||
{
|
||||
int pg;
|
||||
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
pg = get_order(size);
|
||||
dec_snd_pages(pg);
|
||||
unmark_pages(virt_to_page(ptr), pg);
|
||||
free_pages((unsigned long) ptr, pg);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Bus-specific memory allocators
|
||||
*
|
||||
*/
|
||||
|
||||
/* allocate the coherent DMA pages */
|
||||
static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
|
||||
{
|
||||
int pg;
|
||||
void *res;
|
||||
unsigned int gfp_flags;
|
||||
|
||||
snd_assert(size > 0, return NULL);
|
||||
snd_assert(dma != NULL, return NULL);
|
||||
pg = get_order(size);
|
||||
gfp_flags = GFP_KERNEL
|
||||
| __GFP_NORETRY /* don't trigger OOM-killer */
|
||||
| __GFP_NOWARN; /* no stack trace print - this call is non-critical */
|
||||
res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
|
||||
if (res != NULL) {
|
||||
#ifdef NEED_RESERVE_PAGES
|
||||
mark_pages(virt_to_page(res), pg); /* should be dma_to_page() */
|
||||
#endif
|
||||
inc_snd_pages(pg);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* free the coherent DMA pages */
|
||||
static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
|
||||
dma_addr_t dma)
|
||||
{
|
||||
int pg;
|
||||
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
pg = get_order(size);
|
||||
dec_snd_pages(pg);
|
||||
#ifdef NEED_RESERVE_PAGES
|
||||
unmark_pages(virt_to_page(ptr), pg); /* should be dma_to_page() */
|
||||
#endif
|
||||
dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SBUS
|
||||
|
||||
static void *snd_malloc_sbus_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_addr)
|
||||
{
|
||||
struct sbus_dev *sdev = (struct sbus_dev *)dev;
|
||||
int pg;
|
||||
void *res;
|
||||
|
||||
snd_assert(size > 0, return NULL);
|
||||
snd_assert(dma_addr != NULL, return NULL);
|
||||
pg = get_order(size);
|
||||
res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
|
||||
if (res != NULL)
|
||||
inc_snd_pages(pg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void snd_free_sbus_pages(struct device *dev, size_t size,
|
||||
void *ptr, dma_addr_t dma_addr)
|
||||
{
|
||||
struct sbus_dev *sdev = (struct sbus_dev *)dev;
|
||||
int pg;
|
||||
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
pg = get_order(size);
|
||||
dec_snd_pages(pg);
|
||||
sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SBUS */
|
||||
|
||||
/*
|
||||
*
|
||||
* ALSA generic memory management
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* snd_dma_alloc_pages - allocate the buffer area according to the given type
|
||||
* @type: the DMA buffer type
|
||||
* @device: the device pointer
|
||||
* @size: the buffer size to allocate
|
||||
* @dmab: buffer allocation record to store the allocated data
|
||||
*
|
||||
* Calls the memory-allocator function for the corresponding
|
||||
* buffer type.
|
||||
*
|
||||
* Returns zero if the buffer with the given size is allocated successfuly,
|
||||
* other a negative value at error.
|
||||
*/
|
||||
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
|
||||
struct snd_dma_buffer *dmab)
|
||||
{
|
||||
snd_assert(size > 0, return -ENXIO);
|
||||
snd_assert(dmab != NULL, return -ENXIO);
|
||||
|
||||
dmab->dev.type = type;
|
||||
dmab->dev.dev = device;
|
||||
dmab->bytes = 0;
|
||||
switch (type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
dmab->area = snd_malloc_pages(size, (unsigned long)device);
|
||||
dmab->addr = 0;
|
||||
break;
|
||||
#ifdef CONFIG_SBUS
|
||||
case SNDRV_DMA_TYPE_SBUS:
|
||||
dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
|
||||
break;
|
||||
#endif
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
|
||||
break;
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
|
||||
dmab->area = NULL;
|
||||
dmab->addr = 0;
|
||||
return -ENXIO;
|
||||
}
|
||||
if (! dmab->area)
|
||||
return -ENOMEM;
|
||||
dmab->bytes = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
|
||||
* @type: the DMA buffer type
|
||||
* @device: the device pointer
|
||||
* @size: the buffer size to allocate
|
||||
* @dmab: buffer allocation record to store the allocated data
|
||||
*
|
||||
* Calls the memory-allocator function for the corresponding
|
||||
* buffer type. When no space is left, this function reduces the size and
|
||||
* tries to allocate again. The size actually allocated is stored in
|
||||
* res_size argument.
|
||||
*
|
||||
* Returns zero if the buffer with the given size is allocated successfuly,
|
||||
* other a negative value at error.
|
||||
*/
|
||||
int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
|
||||
struct snd_dma_buffer *dmab)
|
||||
{
|
||||
int err;
|
||||
|
||||
snd_assert(size > 0, return -ENXIO);
|
||||
snd_assert(dmab != NULL, return -ENXIO);
|
||||
|
||||
while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
|
||||
if (err != -ENOMEM)
|
||||
return err;
|
||||
size >>= 1;
|
||||
if (size <= PAGE_SIZE)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (! dmab->area)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* snd_dma_free_pages - release the allocated buffer
|
||||
* @dmab: the buffer allocation record to release
|
||||
*
|
||||
* Releases the allocated buffer via snd_dma_alloc_pages().
|
||||
*/
|
||||
void snd_dma_free_pages(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
switch (dmab->dev.type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
snd_free_pages(dmab->area, dmab->bytes);
|
||||
break;
|
||||
#ifdef CONFIG_SBUS
|
||||
case SNDRV_DMA_TYPE_SBUS:
|
||||
snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
|
||||
break;
|
||||
#endif
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
|
||||
break;
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
snd_free_sgbuf_pages(dmab);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* snd_dma_get_reserved - get the reserved buffer for the given device
|
||||
* @dmab: the buffer allocation record to store
|
||||
* @id: the buffer id
|
||||
*
|
||||
* Looks for the reserved-buffer list and re-uses if the same buffer
|
||||
* is found in the list. When the buffer is found, it's removed from the free list.
|
||||
*
|
||||
* Returns the size of buffer if the buffer is found, or zero if not found.
|
||||
*/
|
||||
size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct snd_mem_list *mem;
|
||||
|
||||
snd_assert(dmab, return 0);
|
||||
|
||||
down(&list_mutex);
|
||||
list_for_each(p, &mem_list_head) {
|
||||
mem = list_entry(p, struct snd_mem_list, list);
|
||||
if (mem->id == id &&
|
||||
! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
|
||||
list_del(p);
|
||||
*dmab = mem->buffer;
|
||||
kfree(mem);
|
||||
up(&list_mutex);
|
||||
return dmab->bytes;
|
||||
}
|
||||
}
|
||||
up(&list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_dma_reserve_buf - reserve the buffer
|
||||
* @dmab: the buffer to reserve
|
||||
* @id: the buffer id
|
||||
*
|
||||
* Reserves the given buffer as a reserved buffer.
|
||||
*
|
||||
* Returns zero if successful, or a negative code at error.
|
||||
*/
|
||||
int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
|
||||
{
|
||||
struct snd_mem_list *mem;
|
||||
|
||||
snd_assert(dmab, return -EINVAL);
|
||||
mem = kmalloc(sizeof(*mem), GFP_KERNEL);
|
||||
if (! mem)
|
||||
return -ENOMEM;
|
||||
down(&list_mutex);
|
||||
mem->buffer = *dmab;
|
||||
mem->id = id;
|
||||
list_add_tail(&mem->list, &mem_list_head);
|
||||
up(&list_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* purge all reserved buffers
|
||||
*/
|
||||
static void free_all_reserved_pages(void)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct snd_mem_list *mem;
|
||||
|
||||
down(&list_mutex);
|
||||
while (! list_empty(&mem_list_head)) {
|
||||
p = mem_list_head.next;
|
||||
mem = list_entry(p, struct snd_mem_list, list);
|
||||
list_del(p);
|
||||
snd_dma_free_pages(&mem->buffer);
|
||||
kfree(mem);
|
||||
}
|
||||
up(&list_mutex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* allocation of buffers for pre-defined devices
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* FIXME: for pci only - other bus? */
|
||||
struct prealloc_dev {
|
||||
unsigned short vendor;
|
||||
unsigned short device;
|
||||
unsigned long dma_mask;
|
||||
unsigned int size;
|
||||
unsigned int buffers;
|
||||
};
|
||||
|
||||
#define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)+0x10000)
|
||||
|
||||
static struct prealloc_dev prealloc_devices[] __initdata = {
|
||||
{
|
||||
/* hammerfall */
|
||||
.vendor = 0x10ee,
|
||||
.device = 0x3fc4,
|
||||
.dma_mask = 0xffffffff,
|
||||
.size = HAMMERFALL_BUFFER_SIZE,
|
||||
.buffers = 2
|
||||
},
|
||||
{
|
||||
/* HDSP */
|
||||
.vendor = 0x10ee,
|
||||
.device = 0x3fc5,
|
||||
.dma_mask = 0xffffffff,
|
||||
.size = HAMMERFALL_BUFFER_SIZE,
|
||||
.buffers = 2
|
||||
},
|
||||
{ }, /* terminator */
|
||||
};
|
||||
|
||||
static void __init preallocate_cards(void)
|
||||
{
|
||||
struct pci_dev *pci = NULL;
|
||||
int card;
|
||||
|
||||
card = 0;
|
||||
|
||||
while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
|
||||
struct prealloc_dev *dev;
|
||||
unsigned int i;
|
||||
if (card >= SNDRV_CARDS)
|
||||
break;
|
||||
for (dev = prealloc_devices; dev->vendor; dev++) {
|
||||
if (dev->vendor == pci->vendor && dev->device == pci->device)
|
||||
break;
|
||||
}
|
||||
if (! dev->vendor)
|
||||
continue;
|
||||
if (! enable[card++]) {
|
||||
printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pci_set_dma_mask(pci, dev->dma_mask) < 0 ||
|
||||
pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
|
||||
printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < dev->buffers; i++) {
|
||||
struct snd_dma_buffer dmab;
|
||||
memset(&dmab, 0, sizeof(dmab));
|
||||
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
|
||||
dev->size, &dmab) < 0)
|
||||
printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
|
||||
else
|
||||
snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define preallocate_cards() /* NOP */
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/*
|
||||
* proc file interface
|
||||
*/
|
||||
static int snd_mem_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
int len = 0;
|
||||
long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
|
||||
struct list_head *p;
|
||||
struct snd_mem_list *mem;
|
||||
int devno;
|
||||
static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
|
||||
|
||||
down(&list_mutex);
|
||||
len += snprintf(page + len, count - len,
|
||||
"pages : %li bytes (%li pages per %likB)\n",
|
||||
pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
|
||||
devno = 0;
|
||||
list_for_each(p, &mem_list_head) {
|
||||
mem = list_entry(p, struct snd_mem_list, list);
|
||||
devno++;
|
||||
len += snprintf(page + len, count - len,
|
||||
"buffer %d : ID %08x : type %s\n",
|
||||
devno, mem->id, types[mem->buffer.dev.type]);
|
||||
len += snprintf(page + len, count - len,
|
||||
" addr = 0x%lx, size = %d bytes\n",
|
||||
(unsigned long)mem->buffer.addr, (int)mem->buffer.bytes);
|
||||
}
|
||||
up(&list_mutex);
|
||||
return len;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
/*
|
||||
* module entry
|
||||
*/
|
||||
|
||||
static int __init snd_mem_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
|
||||
#endif
|
||||
preallocate_cards();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit snd_mem_exit(void)
|
||||
{
|
||||
remove_proc_entry("driver/snd-page-alloc", NULL);
|
||||
free_all_reserved_pages();
|
||||
if (snd_allocated_pages > 0)
|
||||
printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages);
|
||||
}
|
||||
|
||||
|
||||
module_init(snd_mem_init)
|
||||
module_exit(snd_mem_exit)
|
||||
|
||||
|
||||
/*
|
||||
* exports
|
||||
*/
|
||||
EXPORT_SYMBOL(snd_dma_alloc_pages);
|
||||
EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
|
||||
EXPORT_SYMBOL(snd_dma_free_pages);
|
||||
|
||||
EXPORT_SYMBOL(snd_dma_get_reserved_buf);
|
||||
EXPORT_SYMBOL(snd_dma_reserve_buf);
|
||||
|
||||
EXPORT_SYMBOL(snd_malloc_pages);
|
||||
EXPORT_SYMBOL(snd_free_pages);
|
||||
306
sound/core/memory.c
Normal file
306
sound/core/memory.c
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* Memory allocation helpers.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/pci.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
/*
|
||||
* memory allocation helpers and debug routines
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG_MEMORY
|
||||
|
||||
struct snd_alloc_track {
|
||||
unsigned long magic;
|
||||
void *caller;
|
||||
size_t size;
|
||||
struct list_head list;
|
||||
long data[0];
|
||||
};
|
||||
|
||||
#define snd_alloc_track_entry(obj) (struct snd_alloc_track *)((char*)obj - (unsigned long)((struct snd_alloc_track *)0)->data)
|
||||
|
||||
static long snd_alloc_kmalloc;
|
||||
static long snd_alloc_vmalloc;
|
||||
static LIST_HEAD(snd_alloc_kmalloc_list);
|
||||
static LIST_HEAD(snd_alloc_vmalloc_list);
|
||||
static DEFINE_SPINLOCK(snd_alloc_kmalloc_lock);
|
||||
static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock);
|
||||
#define KMALLOC_MAGIC 0x87654321
|
||||
#define VMALLOC_MAGIC 0x87654320
|
||||
static snd_info_entry_t *snd_memory_info_entry;
|
||||
|
||||
void snd_memory_init(void)
|
||||
{
|
||||
snd_alloc_kmalloc = 0;
|
||||
snd_alloc_vmalloc = 0;
|
||||
}
|
||||
|
||||
void snd_memory_done(void)
|
||||
{
|
||||
struct list_head *head;
|
||||
struct snd_alloc_track *t;
|
||||
|
||||
if (snd_alloc_kmalloc > 0)
|
||||
snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc);
|
||||
if (snd_alloc_vmalloc > 0)
|
||||
snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc);
|
||||
list_for_each_prev(head, &snd_alloc_kmalloc_list) {
|
||||
t = list_entry(head, struct snd_alloc_track, list);
|
||||
if (t->magic != KMALLOC_MAGIC) {
|
||||
snd_printk(KERN_ERR "Corrupted kmalloc\n");
|
||||
break;
|
||||
}
|
||||
snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
|
||||
}
|
||||
list_for_each_prev(head, &snd_alloc_vmalloc_list) {
|
||||
t = list_entry(head, struct snd_alloc_track, list);
|
||||
if (t->magic != VMALLOC_MAGIC) {
|
||||
snd_printk(KERN_ERR "Corrupted vmalloc\n");
|
||||
break;
|
||||
}
|
||||
snd_printk(KERN_ERR "vmalloc(%ld) from %p not freed\n", (long) t->size, t->caller);
|
||||
}
|
||||
}
|
||||
|
||||
static void *__snd_kmalloc(size_t size, int flags, void *caller)
|
||||
{
|
||||
unsigned long cpu_flags;
|
||||
struct snd_alloc_track *t;
|
||||
void *ptr;
|
||||
|
||||
ptr = snd_wrapper_kmalloc(size + sizeof(struct snd_alloc_track), flags);
|
||||
if (ptr != NULL) {
|
||||
t = (struct snd_alloc_track *)ptr;
|
||||
t->magic = KMALLOC_MAGIC;
|
||||
t->caller = caller;
|
||||
spin_lock_irqsave(&snd_alloc_kmalloc_lock, cpu_flags);
|
||||
list_add_tail(&t->list, &snd_alloc_kmalloc_list);
|
||||
spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, cpu_flags);
|
||||
t->size = size;
|
||||
snd_alloc_kmalloc += size;
|
||||
ptr = t->data;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0));
|
||||
void *snd_hidden_kmalloc(size_t size, int flags)
|
||||
{
|
||||
return _snd_kmalloc(size, flags);
|
||||
}
|
||||
|
||||
void *snd_hidden_kcalloc(size_t n, size_t size, int flags)
|
||||
{
|
||||
void *ret = NULL;
|
||||
if (n != 0 && size > INT_MAX / n)
|
||||
return ret;
|
||||
ret = _snd_kmalloc(n * size, flags);
|
||||
if (ret)
|
||||
memset(ret, 0, n * size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void snd_hidden_kfree(const void *obj)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_alloc_track *t;
|
||||
if (obj == NULL)
|
||||
return;
|
||||
t = snd_alloc_track_entry(obj);
|
||||
if (t->magic != KMALLOC_MAGIC) {
|
||||
snd_printk(KERN_WARNING "bad kfree (called from %p)\n", __builtin_return_address(0));
|
||||
return;
|
||||
}
|
||||
spin_lock_irqsave(&snd_alloc_kmalloc_lock, flags);
|
||||
list_del(&t->list);
|
||||
spin_unlock_irqrestore(&snd_alloc_kmalloc_lock, flags);
|
||||
t->magic = 0;
|
||||
snd_alloc_kmalloc -= t->size;
|
||||
obj = t;
|
||||
snd_wrapper_kfree(obj);
|
||||
}
|
||||
|
||||
void *snd_hidden_vmalloc(unsigned long size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = snd_wrapper_vmalloc(size + sizeof(struct snd_alloc_track));
|
||||
if (ptr) {
|
||||
struct snd_alloc_track *t = (struct snd_alloc_track *)ptr;
|
||||
t->magic = VMALLOC_MAGIC;
|
||||
t->caller = __builtin_return_address(0);
|
||||
spin_lock(&snd_alloc_vmalloc_lock);
|
||||
list_add_tail(&t->list, &snd_alloc_vmalloc_list);
|
||||
spin_unlock(&snd_alloc_vmalloc_lock);
|
||||
t->size = size;
|
||||
snd_alloc_vmalloc += size;
|
||||
ptr = t->data;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void snd_hidden_vfree(void *obj)
|
||||
{
|
||||
struct snd_alloc_track *t;
|
||||
if (obj == NULL)
|
||||
return;
|
||||
t = snd_alloc_track_entry(obj);
|
||||
if (t->magic != VMALLOC_MAGIC) {
|
||||
snd_printk(KERN_ERR "bad vfree (called from %p)\n", __builtin_return_address(0));
|
||||
return;
|
||||
}
|
||||
spin_lock(&snd_alloc_vmalloc_lock);
|
||||
list_del(&t->list);
|
||||
spin_unlock(&snd_alloc_vmalloc_lock);
|
||||
t->magic = 0;
|
||||
snd_alloc_vmalloc -= t->size;
|
||||
obj = t;
|
||||
snd_wrapper_vfree(obj);
|
||||
}
|
||||
|
||||
static void snd_memory_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
snd_iprintf(buffer, "kmalloc: %li bytes\n", snd_alloc_kmalloc);
|
||||
snd_iprintf(buffer, "vmalloc: %li bytes\n", snd_alloc_vmalloc);
|
||||
}
|
||||
|
||||
int __init snd_memory_info_init(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "meminfo", NULL);
|
||||
if (entry) {
|
||||
entry->c.text.read_size = 256;
|
||||
entry->c.text.read = snd_memory_info_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
snd_memory_info_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_memory_info_done(void)
|
||||
{
|
||||
if (snd_memory_info_entry)
|
||||
snd_info_unregister(snd_memory_info_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define _snd_kmalloc kmalloc
|
||||
|
||||
#endif /* CONFIG_SND_DEBUG_MEMORY */
|
||||
|
||||
/**
|
||||
* snd_kmalloc_strdup - copy the string
|
||||
* @string: the original string
|
||||
* @flags: allocation conditions, GFP_XXX
|
||||
*
|
||||
* Allocates a memory chunk via kmalloc() and copies the string to it.
|
||||
*
|
||||
* Returns the pointer, or NULL if no enoguh memory.
|
||||
*/
|
||||
char *snd_kmalloc_strdup(const char *string, int flags)
|
||||
{
|
||||
size_t len;
|
||||
char *ptr;
|
||||
|
||||
if (!string)
|
||||
return NULL;
|
||||
len = strlen(string) + 1;
|
||||
ptr = _snd_kmalloc(len, flags);
|
||||
if (ptr)
|
||||
memcpy(ptr, string, len);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_to_user_fromio - copy data from mmio-space to user-space
|
||||
* @dst: the destination pointer on user-space
|
||||
* @src: the source pointer on mmio
|
||||
* @count: the data size to copy in bytes
|
||||
*
|
||||
* Copies the data from mmio-space to user-space.
|
||||
*
|
||||
* Returns zero if successful, or non-zero on failure.
|
||||
*/
|
||||
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
|
||||
{
|
||||
#if defined(__i386__) || defined(CONFIG_SPARC32)
|
||||
return copy_to_user(dst, (const void*)src, count) ? -EFAULT : 0;
|
||||
#else
|
||||
char buf[256];
|
||||
while (count) {
|
||||
size_t c = count;
|
||||
if (c > sizeof(buf))
|
||||
c = sizeof(buf);
|
||||
memcpy_fromio(buf, (void __iomem *)src, c);
|
||||
if (copy_to_user(dst, buf, c))
|
||||
return -EFAULT;
|
||||
count -= c;
|
||||
dst += c;
|
||||
src += c;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_from_user_toio - copy data from user-space to mmio-space
|
||||
* @dst: the destination pointer on mmio-space
|
||||
* @src: the source pointer on user-space
|
||||
* @count: the data size to copy in bytes
|
||||
*
|
||||
* Copies the data from user-space to mmio-space.
|
||||
*
|
||||
* Returns zero if successful, or non-zero on failure.
|
||||
*/
|
||||
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
|
||||
{
|
||||
#if defined(__i386__) || defined(CONFIG_SPARC32)
|
||||
return copy_from_user((void*)dst, src, count) ? -EFAULT : 0;
|
||||
#else
|
||||
char buf[256];
|
||||
while (count) {
|
||||
size_t c = count;
|
||||
if (c > sizeof(buf))
|
||||
c = sizeof(buf);
|
||||
if (copy_from_user(buf, src, c))
|
||||
return -EFAULT;
|
||||
memcpy_toio(dst, buf, c);
|
||||
count -= c;
|
||||
dst += c;
|
||||
src += c;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
76
sound/core/misc.c
Normal file
76
sound/core/misc.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Misc and compatibility things
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
int snd_task_name(struct task_struct *task, char *name, size_t size)
|
||||
{
|
||||
unsigned int idx;
|
||||
|
||||
snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL);
|
||||
for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)
|
||||
name[idx] = task->comm[idx];
|
||||
name[idx] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
void snd_verbose_printk(const char *file, int line, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
format += 3;
|
||||
} else {
|
||||
printk("ALSA %s:%d: ", file, line);
|
||||
}
|
||||
va_start(args, format);
|
||||
vprintk(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
format += 3;
|
||||
} else {
|
||||
printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
|
||||
}
|
||||
va_start(args, format);
|
||||
vprintk(format, args);
|
||||
va_end(args);
|
||||
|
||||
}
|
||||
#endif
|
||||
12
sound/core/oss/Makefile
Normal file
12
sound/core/oss/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
#
|
||||
|
||||
snd-mixer-oss-objs := mixer_oss.o
|
||||
|
||||
snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \
|
||||
io.o copy.o linear.o mulaw.o route.o rate.o
|
||||
|
||||
obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o
|
||||
obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o
|
||||
87
sound/core/oss/copy.c
Normal file
87
sound/core/oss/copy.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Linear conversion Plug-In
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
static snd_pcm_sframes_t copy_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
unsigned int channel;
|
||||
unsigned int nchannels;
|
||||
|
||||
snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
snd_assert(src_channels->area.first % 8 == 0 &&
|
||||
src_channels->area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
snd_assert(dst_channels->area.first % 8 == 0 &&
|
||||
dst_channels->area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
if (!src_channels->enabled) {
|
||||
if (dst_channels->wanted)
|
||||
snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels->enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels->enabled = 1;
|
||||
snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format);
|
||||
src_channels++;
|
||||
dst_channels++;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
int width;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
|
||||
snd_assert(src_format->format == dst_format->format, return -ENXIO);
|
||||
snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
|
||||
snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
|
||||
|
||||
width = snd_pcm_format_physical_width(src_format->format);
|
||||
snd_assert(width > 0, return -ENXIO);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,
|
||||
0, &plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
plugin->transfer = copy_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
134
sound/core/oss/io.c
Normal file
134
sound/core/oss/io.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* PCM I/O Plug-In Interface
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
|
||||
#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
|
||||
#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
|
||||
#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
|
||||
|
||||
/*
|
||||
* Basic io plugin
|
||||
*/
|
||||
|
||||
static snd_pcm_sframes_t io_playback_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
snd_assert(plugin != NULL, return -ENXIO);
|
||||
snd_assert(src_channels != NULL, return -ENXIO);
|
||||
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
return pcm_write(plugin->plug, src_channels->area.addr, frames);
|
||||
} else {
|
||||
int channel, channels = plugin->dst_format.channels;
|
||||
void **bufs = (void**)plugin->extra_data;
|
||||
snd_assert(bufs != NULL, return -ENXIO);
|
||||
for (channel = 0; channel < channels; channel++) {
|
||||
if (src_channels[channel].enabled)
|
||||
bufs[channel] = src_channels[channel].area.addr;
|
||||
else
|
||||
bufs[channel] = NULL;
|
||||
}
|
||||
return pcm_writev(plugin->plug, bufs, frames);
|
||||
}
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t io_capture_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
snd_assert(plugin != NULL, return -ENXIO);
|
||||
snd_assert(dst_channels != NULL, return -ENXIO);
|
||||
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
return pcm_read(plugin->plug, dst_channels->area.addr, frames);
|
||||
} else {
|
||||
int channel, channels = plugin->dst_format.channels;
|
||||
void **bufs = (void**)plugin->extra_data;
|
||||
snd_assert(bufs != NULL, return -ENXIO);
|
||||
for (channel = 0; channel < channels; channel++) {
|
||||
if (dst_channels[channel].enabled)
|
||||
bufs[channel] = dst_channels[channel].area.addr;
|
||||
else
|
||||
bufs[channel] = NULL;
|
||||
}
|
||||
return pcm_readv(plugin->plug, bufs, frames);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t io_src_channels(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_uframes_t frames,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
int err;
|
||||
unsigned int channel;
|
||||
snd_pcm_plugin_channel_t *v;
|
||||
err = snd_pcm_plugin_client_channels(plugin, frames, &v);
|
||||
if (err < 0)
|
||||
return err;
|
||||
*channels = v;
|
||||
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v)
|
||||
v->wanted = 1;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug,
|
||||
snd_pcm_hw_params_t *params,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
snd_pcm_plugin_format_t format;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
snd_assert(plug != NULL && params != NULL, return -ENXIO);
|
||||
format.format = params_format(params);
|
||||
format.rate = params_rate(params);
|
||||
format.channels = params_channels(params);
|
||||
err = snd_pcm_plugin_build(plug, "I/O io",
|
||||
&format, &format,
|
||||
sizeof(void *) * format.channels,
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
plugin->access = params_access(params);
|
||||
if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin->transfer = io_playback_transfer;
|
||||
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
|
||||
plugin->client_channels = io_src_channels;
|
||||
} else {
|
||||
plugin->transfer = io_capture_transfer;
|
||||
}
|
||||
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
158
sound/core/oss/linear.c
Normal file
158
sound/core/oss/linear.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Linear conversion Plug-In
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>,
|
||||
* Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
/*
|
||||
* Basic linear conversion plugin
|
||||
*/
|
||||
|
||||
typedef struct linear_private_data {
|
||||
int conv;
|
||||
} linear_t;
|
||||
|
||||
static void convert(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
linear_t *data = (linear_t *)plugin->extra_data;
|
||||
void *conv = conv_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
snd_pcm_uframes_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t linear_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
linear_t *data;
|
||||
|
||||
snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
|
||||
data = (linear_t *)plugin->extra_data;
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
{
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
snd_assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
snd_assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
convert(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
int conv_index(int src_format, int dst_format)
|
||||
{
|
||||
int src_endian, dst_endian, sign, src_width, dst_width;
|
||||
|
||||
sign = (snd_pcm_format_signed(src_format) !=
|
||||
snd_pcm_format_signed(dst_format));
|
||||
#ifdef SNDRV_LITTLE_ENDIAN
|
||||
src_endian = snd_pcm_format_big_endian(src_format);
|
||||
dst_endian = snd_pcm_format_big_endian(dst_format);
|
||||
#else
|
||||
src_endian = snd_pcm_format_little_endian(src_format);
|
||||
dst_endian = snd_pcm_format_little_endian(dst_format);
|
||||
#endif
|
||||
|
||||
if (src_endian < 0)
|
||||
src_endian = 0;
|
||||
if (dst_endian < 0)
|
||||
dst_endian = 0;
|
||||
|
||||
src_width = snd_pcm_format_width(src_format) / 8 - 1;
|
||||
dst_width = snd_pcm_format_width(dst_format) / 8 - 1;
|
||||
|
||||
return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
struct linear_private_data *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
|
||||
snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
|
||||
snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
|
||||
snd_assert(snd_pcm_format_linear(src_format->format) &&
|
||||
snd_pcm_format_linear(dst_format->format), return -ENXIO);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "linear format conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(linear_t), &plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (linear_t *)plugin->extra_data;
|
||||
data->conv = conv_index(src_format->format, dst_format->format);
|
||||
plugin->transfer = linear_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
1340
sound/core/oss/mixer_oss.c
Normal file
1340
sound/core/oss/mixer_oss.c
Normal file
File diff suppressed because it is too large
Load Diff
308
sound/core/oss/mulaw.c
Normal file
308
sound/core/oss/mulaw.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Mu-Law conversion Plug-In Interface
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
* Uros Bizjak <uros@kss-loka.si>
|
||||
*
|
||||
* Based on reference implementation by Sun Microsystems, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
#define NSEGS (8) /* Number of u-law segments. */
|
||||
#define SEG_SHIFT (4) /* Left shift for segment number. */
|
||||
#define SEG_MASK (0x70) /* Segment field mask. */
|
||||
|
||||
static inline int val_seg(int val)
|
||||
{
|
||||
int r = 0;
|
||||
val >>= 7;
|
||||
if (val & 0xf0) {
|
||||
val >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (val & 0x0c) {
|
||||
val >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (val & 0x02)
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
#define BIAS (0x84) /* Bias for linear code. */
|
||||
|
||||
/*
|
||||
* linear2ulaw() - Convert a linear PCM value to u-law
|
||||
*
|
||||
* In order to simplify the encoding process, the original linear magnitude
|
||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
||||
* (33 - 8191). The result can be seen in the following encoding table:
|
||||
*
|
||||
* Biased Linear Input Code Compressed Code
|
||||
* ------------------------ ---------------
|
||||
* 00000001wxyza 000wxyz
|
||||
* 0000001wxyzab 001wxyz
|
||||
* 000001wxyzabc 010wxyz
|
||||
* 00001wxyzabcd 011wxyz
|
||||
* 0001wxyzabcde 100wxyz
|
||||
* 001wxyzabcdef 101wxyz
|
||||
* 01wxyzabcdefg 110wxyz
|
||||
* 1wxyzabcdefgh 111wxyz
|
||||
*
|
||||
* Each biased linear code has a leading 1 which identifies the segment
|
||||
* number. The value of the segment number is equal to 7 minus the number
|
||||
* of leading 0's. The quantization interval is directly available as the
|
||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
||||
*
|
||||
* Ordinarily the complement of the resulting code word is used for
|
||||
* transmission, and so the code word is complemented before it is returned.
|
||||
*
|
||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
||||
*/
|
||||
static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
||||
{
|
||||
int mask;
|
||||
int seg;
|
||||
unsigned char uval;
|
||||
|
||||
/* Get the sign and the magnitude of the value. */
|
||||
if (pcm_val < 0) {
|
||||
pcm_val = BIAS - pcm_val;
|
||||
mask = 0x7F;
|
||||
} else {
|
||||
pcm_val += BIAS;
|
||||
mask = 0xFF;
|
||||
}
|
||||
if (pcm_val > 0x7FFF)
|
||||
pcm_val = 0x7FFF;
|
||||
|
||||
/* Convert the scaled magnitude to segment number. */
|
||||
seg = val_seg(pcm_val);
|
||||
|
||||
/*
|
||||
* Combine the sign, segment, quantization bits;
|
||||
* and complement the code word.
|
||||
*/
|
||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
|
||||
return uval ^ mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
|
||||
*
|
||||
* First, a biased linear code is derived from the code word. An unbiased
|
||||
* output can then be obtained by subtracting 33 from the biased code.
|
||||
*
|
||||
* Note that this function expects to be passed the complement of the
|
||||
* original code word. This is in keeping with ISDN conventions.
|
||||
*/
|
||||
static int ulaw2linear(unsigned char u_val)
|
||||
{
|
||||
int t;
|
||||
|
||||
/* Complement to obtain normal u-law value. */
|
||||
u_val = ~u_val;
|
||||
|
||||
/*
|
||||
* Extract and bias the quantization bits. Then
|
||||
* shift up by the segment number and subtract out the bias.
|
||||
*/
|
||||
t = ((u_val & QUANT_MASK) << 3) + BIAS;
|
||||
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
|
||||
|
||||
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic Mu-Law plugin
|
||||
*/
|
||||
|
||||
typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames);
|
||||
|
||||
typedef struct mulaw_private_data {
|
||||
mulaw_f func;
|
||||
int conv;
|
||||
} mulaw_t;
|
||||
|
||||
static void mulaw_decode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_LABELS
|
||||
mulaw_t *data = (mulaw_t *)plugin->extra_data;
|
||||
void *put = put_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
snd_pcm_uframes_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
signed short sample = ulaw2linear(*src);
|
||||
goto *put;
|
||||
#define PUT_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mulaw_encode(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
#define GET_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
mulaw_t *data = (mulaw_t *)plugin->extra_data;
|
||||
void *get = get_s16_labels[data->conv];
|
||||
int channel;
|
||||
int nchannels = plugin->src_format.channels;
|
||||
signed short sample = 0;
|
||||
for (channel = 0; channel < nchannels; ++channel) {
|
||||
char *src;
|
||||
char *dst;
|
||||
int src_step, dst_step;
|
||||
snd_pcm_uframes_t frames1;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
frames1 = frames;
|
||||
while (frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after:
|
||||
*dst = linear2ulaw(sample);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t mulaw_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
mulaw_t *data;
|
||||
|
||||
snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
{
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
snd_assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
snd_assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
data = (mulaw_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames);
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
mulaw_t *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
snd_pcm_plugin_format_t *format;
|
||||
mulaw_f func;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
|
||||
snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
|
||||
snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
|
||||
|
||||
if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
|
||||
format = src_format;
|
||||
func = mulaw_encode;
|
||||
}
|
||||
else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
|
||||
format = dst_format;
|
||||
func = mulaw_decode;
|
||||
}
|
||||
else {
|
||||
snd_BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(mulaw_t), &plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (mulaw_t*)plugin->extra_data;
|
||||
data->func = func;
|
||||
data->conv = getput_index(format->format);
|
||||
snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL);
|
||||
plugin->transfer = mulaw_transfer;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
2530
sound/core/oss/pcm_oss.c
Normal file
2530
sound/core/oss/pcm_oss.c
Normal file
File diff suppressed because it is too large
Load Diff
921
sound/core/oss/pcm_plugin.c
Normal file
921
sound/core/oss/pcm_plugin.c
Normal file
@@ -0,0 +1,921 @@
|
||||
/*
|
||||
* PCM Plug-In shared (kernel/library) code
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define PLUGIN_DEBUG
|
||||
#endif
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)
|
||||
#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)
|
||||
|
||||
static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *dst_vmask,
|
||||
bitset_t **src_vmask)
|
||||
{
|
||||
bitset_t *vmask = plugin->src_vmask;
|
||||
bitset_copy(vmask, dst_vmask, plugin->src_format.channels);
|
||||
*src_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *src_vmask,
|
||||
bitset_t **dst_vmask)
|
||||
{
|
||||
bitset_t *vmask = plugin->dst_vmask;
|
||||
bitset_copy(vmask, src_vmask, plugin->dst_format.channels);
|
||||
*dst_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* because some cards might have rates "very close", we ignore
|
||||
* all "resampling" requests within +-5%
|
||||
*/
|
||||
static int rate_match(unsigned int src_rate, unsigned int dst_rate)
|
||||
{
|
||||
unsigned int low = (src_rate * 95) / 100;
|
||||
unsigned int high = (src_rate * 105) / 100;
|
||||
return dst_rate >= low && dst_rate <= high;
|
||||
}
|
||||
|
||||
static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
|
||||
{
|
||||
snd_pcm_plugin_format_t *format;
|
||||
ssize_t width;
|
||||
size_t size;
|
||||
unsigned int channel;
|
||||
snd_pcm_plugin_channel_t *c;
|
||||
|
||||
if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
format = &plugin->src_format;
|
||||
} else {
|
||||
format = &plugin->dst_format;
|
||||
}
|
||||
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
|
||||
return width;
|
||||
size = frames * format->channels * width;
|
||||
snd_assert((size % 8) == 0, return -ENXIO);
|
||||
size /= 8;
|
||||
if (plugin->buf_frames < frames) {
|
||||
vfree(plugin->buf);
|
||||
plugin->buf = vmalloc(size);
|
||||
plugin->buf_frames = frames;
|
||||
}
|
||||
if (!plugin->buf) {
|
||||
plugin->buf_frames = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
c = plugin->buf_channels;
|
||||
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
|
||||
for (channel = 0; channel < format->channels; channel++, c++) {
|
||||
c->frames = frames;
|
||||
c->enabled = 1;
|
||||
c->wanted = 0;
|
||||
c->area.addr = plugin->buf;
|
||||
c->area.first = channel * width;
|
||||
c->area.step = format->channels * width;
|
||||
}
|
||||
} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||
snd_assert((size % format->channels) == 0,);
|
||||
size /= format->channels;
|
||||
for (channel = 0; channel < format->channels; channel++, c++) {
|
||||
c->frames = frames;
|
||||
c->enabled = 1;
|
||||
c->wanted = 0;
|
||||
c->area.addr = plugin->buf + (channel * size);
|
||||
c->area.first = 0;
|
||||
c->area.step = width;
|
||||
}
|
||||
} else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames)
|
||||
{
|
||||
int err;
|
||||
snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO);
|
||||
if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin->next) {
|
||||
if (plugin->dst_frames)
|
||||
frames = plugin->dst_frames(plugin, frames);
|
||||
snd_assert(frames > 0, return -ENXIO);
|
||||
plugin = plugin->next;
|
||||
err = snd_pcm_plugin_alloc(plugin, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin->prev) {
|
||||
if (plugin->src_frames)
|
||||
frames = plugin->src_frames(plugin, frames);
|
||||
snd_assert(frames > 0, return -ENXIO);
|
||||
plugin = plugin->prev;
|
||||
err = snd_pcm_plugin_alloc(plugin, frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_uframes_t frames,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
*channels = plugin->buf_channels;
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build(snd_pcm_plug_t *plug,
|
||||
const char *name,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
size_t extra,
|
||||
snd_pcm_plugin_t **ret)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin;
|
||||
unsigned int channels;
|
||||
|
||||
snd_assert(plug != NULL, return -ENXIO);
|
||||
snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO);
|
||||
plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL);
|
||||
if (plugin == NULL)
|
||||
return -ENOMEM;
|
||||
plugin->name = name;
|
||||
plugin->plug = plug;
|
||||
plugin->stream = snd_pcm_plug_stream(plug);
|
||||
plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
|
||||
plugin->src_format = *src_format;
|
||||
plugin->src_width = snd_pcm_format_physical_width(src_format->format);
|
||||
snd_assert(plugin->src_width > 0, );
|
||||
plugin->dst_format = *dst_format;
|
||||
plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);
|
||||
snd_assert(plugin->dst_width > 0, );
|
||||
if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
channels = src_format->channels;
|
||||
else
|
||||
channels = dst_format->channels;
|
||||
plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);
|
||||
if (plugin->buf_channels == NULL) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return -ENOMEM;
|
||||
}
|
||||
plugin->src_vmask = bitset_alloc(src_format->channels);
|
||||
if (plugin->src_vmask == NULL) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return -ENOMEM;
|
||||
}
|
||||
plugin->dst_vmask = bitset_alloc(dst_format->channels);
|
||||
if (plugin->dst_vmask == NULL) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return -ENOMEM;
|
||||
}
|
||||
plugin->client_channels = snd_pcm_plugin_client_channels;
|
||||
plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask;
|
||||
plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask;
|
||||
*ret = plugin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
if (! plugin)
|
||||
return 0;
|
||||
if (plugin->private_free)
|
||||
plugin->private_free(plugin);
|
||||
kfree(plugin->buf_channels);
|
||||
vfree(plugin->buf);
|
||||
kfree(plugin->src_vmask);
|
||||
kfree(plugin->dst_vmask);
|
||||
kfree(plugin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
|
||||
int stream = snd_pcm_plug_stream(plug);
|
||||
|
||||
snd_assert(plug != NULL, return -ENXIO);
|
||||
if (drv_frames == 0)
|
||||
return 0;
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin && drv_frames > 0) {
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_frames)
|
||||
drv_frames = plugin->src_frames(plugin, drv_frames);
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && drv_frames > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_frames)
|
||||
drv_frames = plugin->dst_frames(plugin, drv_frames);
|
||||
plugin = plugin_next;
|
||||
}
|
||||
} else
|
||||
snd_BUG();
|
||||
return drv_frames;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next;
|
||||
snd_pcm_sframes_t frames;
|
||||
int stream = snd_pcm_plug_stream(plug);
|
||||
|
||||
snd_assert(plug != NULL, return -ENXIO);
|
||||
if (clt_frames == 0)
|
||||
return 0;
|
||||
frames = clt_frames;
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
plugin_next = plugin->next;
|
||||
if (plugin->dst_frames) {
|
||||
frames = plugin->dst_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
plugin = plugin_next;
|
||||
}
|
||||
} else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
while (plugin) {
|
||||
plugin_prev = plugin->prev;
|
||||
if (plugin->src_frames) {
|
||||
frames = plugin->src_frames(plugin, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
}
|
||||
plugin = plugin_prev;
|
||||
}
|
||||
} else
|
||||
snd_BUG();
|
||||
return frames;
|
||||
}
|
||||
|
||||
static int snd_pcm_plug_formats(snd_mask_t *mask, int format)
|
||||
{
|
||||
snd_mask_t formats = *mask;
|
||||
u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
|
||||
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
|
||||
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
|
||||
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
|
||||
snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
|
||||
|
||||
if (formats.bits[0] & (u32)linfmts)
|
||||
formats.bits[0] |= (u32)linfmts;
|
||||
if (formats.bits[1] & (u32)(linfmts >> 32))
|
||||
formats.bits[1] |= (u32)(linfmts >> 32);
|
||||
return snd_mask_test(&formats, format);
|
||||
}
|
||||
|
||||
static int preferred_formats[] = {
|
||||
SNDRV_PCM_FORMAT_S16_LE,
|
||||
SNDRV_PCM_FORMAT_S16_BE,
|
||||
SNDRV_PCM_FORMAT_U16_LE,
|
||||
SNDRV_PCM_FORMAT_U16_BE,
|
||||
SNDRV_PCM_FORMAT_S24_LE,
|
||||
SNDRV_PCM_FORMAT_S24_BE,
|
||||
SNDRV_PCM_FORMAT_U24_LE,
|
||||
SNDRV_PCM_FORMAT_U24_BE,
|
||||
SNDRV_PCM_FORMAT_S32_LE,
|
||||
SNDRV_PCM_FORMAT_S32_BE,
|
||||
SNDRV_PCM_FORMAT_U32_LE,
|
||||
SNDRV_PCM_FORMAT_U32_BE,
|
||||
SNDRV_PCM_FORMAT_S8,
|
||||
SNDRV_PCM_FORMAT_U8
|
||||
};
|
||||
|
||||
int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask)
|
||||
{
|
||||
if (snd_mask_test(format_mask, format))
|
||||
return format;
|
||||
if (! snd_pcm_plug_formats(format_mask, format))
|
||||
return -EINVAL;
|
||||
if (snd_pcm_format_linear(format)) {
|
||||
int width = snd_pcm_format_width(format);
|
||||
int unsignd = snd_pcm_format_unsigned(format);
|
||||
int big = snd_pcm_format_big_endian(format);
|
||||
int format1;
|
||||
int wid, width1=width;
|
||||
int dwidth1 = 8;
|
||||
for (wid = 0; wid < 4; ++wid) {
|
||||
int end, big1 = big;
|
||||
for (end = 0; end < 2; ++end) {
|
||||
int sgn, unsignd1 = unsignd;
|
||||
for (sgn = 0; sgn < 2; ++sgn) {
|
||||
format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
|
||||
if (format1 >= 0 &&
|
||||
snd_mask_test(format_mask, format1))
|
||||
goto _found;
|
||||
unsignd1 = !unsignd1;
|
||||
}
|
||||
big1 = !big1;
|
||||
}
|
||||
if (width1 == 32) {
|
||||
dwidth1 = -dwidth1;
|
||||
width1 = width;
|
||||
}
|
||||
width1 += dwidth1;
|
||||
}
|
||||
return -EINVAL;
|
||||
_found:
|
||||
return format1;
|
||||
} else {
|
||||
unsigned int i;
|
||||
switch (format) {
|
||||
case SNDRV_PCM_FORMAT_MU_LAW:
|
||||
for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
|
||||
int format1 = preferred_formats[i];
|
||||
if (snd_mask_test(format_mask, format1))
|
||||
return format1;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug,
|
||||
snd_pcm_hw_params_t *params,
|
||||
snd_pcm_hw_params_t *slave_params)
|
||||
{
|
||||
snd_pcm_plugin_format_t tmpformat;
|
||||
snd_pcm_plugin_format_t dstformat;
|
||||
snd_pcm_plugin_format_t srcformat;
|
||||
int src_access, dst_access;
|
||||
snd_pcm_plugin_t *plugin = NULL;
|
||||
int err;
|
||||
int stream = snd_pcm_plug_stream(plug);
|
||||
int slave_interleaved = (params_channels(slave_params) == 1 ||
|
||||
params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);
|
||||
|
||||
switch (stream) {
|
||||
case SNDRV_PCM_STREAM_PLAYBACK:
|
||||
dstformat.format = params_format(slave_params);
|
||||
dstformat.rate = params_rate(slave_params);
|
||||
dstformat.channels = params_channels(slave_params);
|
||||
srcformat.format = params_format(params);
|
||||
srcformat.rate = params_rate(params);
|
||||
srcformat.channels = params_channels(params);
|
||||
src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
|
||||
dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
|
||||
SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
|
||||
break;
|
||||
case SNDRV_PCM_STREAM_CAPTURE:
|
||||
dstformat.format = params_format(params);
|
||||
dstformat.rate = params_rate(params);
|
||||
dstformat.channels = params_channels(params);
|
||||
srcformat.format = params_format(slave_params);
|
||||
srcformat.rate = params_rate(slave_params);
|
||||
srcformat.channels = params_channels(slave_params);
|
||||
src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :
|
||||
SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
|
||||
dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
|
||||
break;
|
||||
default:
|
||||
snd_BUG();
|
||||
return -EINVAL;
|
||||
}
|
||||
tmpformat = srcformat;
|
||||
|
||||
pdprintf("srcformat: format=%i, rate=%i, channels=%i\n",
|
||||
srcformat.format,
|
||||
srcformat.rate,
|
||||
srcformat.channels);
|
||||
pdprintf("dstformat: format=%i, rate=%i, channels=%i\n",
|
||||
dstformat.format,
|
||||
dstformat.rate,
|
||||
dstformat.channels);
|
||||
|
||||
/* Format change (linearization) */
|
||||
if ((srcformat.format != dstformat.format ||
|
||||
!rate_match(srcformat.rate, dstformat.rate) ||
|
||||
srcformat.channels != dstformat.channels) &&
|
||||
!snd_pcm_format_linear(srcformat.format)) {
|
||||
if (snd_pcm_format_linear(dstformat.format))
|
||||
tmpformat.format = dstformat.format;
|
||||
else
|
||||
tmpformat.format = SNDRV_PCM_FORMAT_S16;
|
||||
switch (srcformat.format) {
|
||||
case SNDRV_PCM_FORMAT_MU_LAW:
|
||||
err = snd_pcm_plugin_build_mulaw(plug,
|
||||
&srcformat, &tmpformat,
|
||||
&plugin);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
srcformat = tmpformat;
|
||||
src_access = dst_access;
|
||||
}
|
||||
|
||||
/* channels reduction */
|
||||
if (srcformat.channels > dstformat.channels) {
|
||||
int sv = srcformat.channels;
|
||||
int dv = dstformat.channels;
|
||||
route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
|
||||
if (ttable == NULL)
|
||||
return -ENOMEM;
|
||||
#if 1
|
||||
if (sv == 2 && dv == 1) {
|
||||
ttable[0] = HALF;
|
||||
ttable[1] = HALF;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
int v;
|
||||
for (v = 0; v < dv; ++v)
|
||||
ttable[v * sv + v] = FULL;
|
||||
}
|
||||
tmpformat.channels = dstformat.channels;
|
||||
if (rate_match(srcformat.rate, dstformat.rate) &&
|
||||
snd_pcm_format_linear(dstformat.format))
|
||||
tmpformat.format = dstformat.format;
|
||||
err = snd_pcm_plugin_build_route(plug,
|
||||
&srcformat, &tmpformat,
|
||||
ttable, &plugin);
|
||||
kfree(ttable);
|
||||
pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
srcformat = tmpformat;
|
||||
src_access = dst_access;
|
||||
}
|
||||
|
||||
/* rate resampling */
|
||||
if (!rate_match(srcformat.rate, dstformat.rate)) {
|
||||
tmpformat.rate = dstformat.rate;
|
||||
if (srcformat.channels == dstformat.channels &&
|
||||
snd_pcm_format_linear(dstformat.format))
|
||||
tmpformat.format = dstformat.format;
|
||||
err = snd_pcm_plugin_build_rate(plug,
|
||||
&srcformat, &tmpformat,
|
||||
&plugin);
|
||||
pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
srcformat = tmpformat;
|
||||
src_access = dst_access;
|
||||
}
|
||||
|
||||
/* channels extension */
|
||||
if (srcformat.channels < dstformat.channels) {
|
||||
int sv = srcformat.channels;
|
||||
int dv = dstformat.channels;
|
||||
route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL);
|
||||
if (ttable == NULL)
|
||||
return -ENOMEM;
|
||||
#if 0
|
||||
{
|
||||
int v;
|
||||
for (v = 0; v < sv; ++v)
|
||||
ttable[v * sv + v] = FULL;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* Playback is spreaded on all channels */
|
||||
int vd, vs;
|
||||
for (vd = 0, vs = 0; vd < dv; ++vd) {
|
||||
ttable[vd * sv + vs] = FULL;
|
||||
vs++;
|
||||
if (vs == sv)
|
||||
vs = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tmpformat.channels = dstformat.channels;
|
||||
if (snd_pcm_format_linear(dstformat.format))
|
||||
tmpformat.format = dstformat.format;
|
||||
err = snd_pcm_plugin_build_route(plug,
|
||||
&srcformat, &tmpformat,
|
||||
ttable, &plugin);
|
||||
kfree(ttable);
|
||||
pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
srcformat = tmpformat;
|
||||
src_access = dst_access;
|
||||
}
|
||||
|
||||
/* format change */
|
||||
if (srcformat.format != dstformat.format) {
|
||||
tmpformat.format = dstformat.format;
|
||||
if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {
|
||||
err = snd_pcm_plugin_build_mulaw(plug,
|
||||
&srcformat, &tmpformat,
|
||||
&plugin);
|
||||
}
|
||||
else if (snd_pcm_format_linear(srcformat.format) &&
|
||||
snd_pcm_format_linear(tmpformat.format)) {
|
||||
err = snd_pcm_plugin_build_linear(plug,
|
||||
&srcformat, &tmpformat,
|
||||
&plugin);
|
||||
}
|
||||
else
|
||||
return -EINVAL;
|
||||
pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
srcformat = tmpformat;
|
||||
src_access = dst_access;
|
||||
}
|
||||
|
||||
/* de-interleave */
|
||||
if (src_access != dst_access) {
|
||||
err = snd_pcm_plugin_build_copy(plug,
|
||||
&srcformat,
|
||||
&tmpformat,
|
||||
&plugin);
|
||||
pdprintf("interleave change (copy: returns %i)\n", err);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_plugin_append(plugin);
|
||||
if (err < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug,
|
||||
char *buf,
|
||||
snd_pcm_uframes_t count,
|
||||
snd_pcm_plugin_channel_t **channels)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin;
|
||||
snd_pcm_plugin_channel_t *v;
|
||||
snd_pcm_plugin_format_t *format;
|
||||
int width, nchannels, channel;
|
||||
int stream = snd_pcm_plug_stream(plug);
|
||||
|
||||
snd_assert(buf != NULL, return -ENXIO);
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
format = &plugin->src_format;
|
||||
} else {
|
||||
plugin = snd_pcm_plug_last(plug);
|
||||
format = &plugin->dst_format;
|
||||
}
|
||||
v = plugin->buf_channels;
|
||||
*channels = v;
|
||||
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
|
||||
return width;
|
||||
nchannels = format->channels;
|
||||
snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO);
|
||||
for (channel = 0; channel < nchannels; channel++, v++) {
|
||||
v->frames = count;
|
||||
v->enabled = 1;
|
||||
v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);
|
||||
v->area.addr = buf;
|
||||
v->area.first = channel * width;
|
||||
v->area.step = nchannels * width;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
|
||||
bitset_t *client_vmask)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
|
||||
if (plugin == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
int schannels = plugin->dst_format.channels;
|
||||
bitset_t bs[bitset_size(schannels)];
|
||||
bitset_t *srcmask;
|
||||
bitset_t *dstmask = bs;
|
||||
int err;
|
||||
bitset_one(dstmask, schannels);
|
||||
if (plugin == NULL) {
|
||||
bitset_and(client_vmask, dstmask, schannels);
|
||||
return 0;
|
||||
}
|
||||
while (1) {
|
||||
err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dstmask = srcmask;
|
||||
if (plugin->prev == NULL)
|
||||
break;
|
||||
plugin = plugin->prev;
|
||||
}
|
||||
bitset_and(client_vmask, dstmask, plugin->src_format.channels);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_channel_t *src_channels)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug);
|
||||
unsigned int nchannels = plugin->src_format.channels;
|
||||
bitset_t bs[bitset_size(nchannels)];
|
||||
bitset_t *srcmask = bs;
|
||||
int err;
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
if (src_channels[channel].enabled)
|
||||
bitset_set(srcmask, channel);
|
||||
else
|
||||
bitset_reset(srcmask, channel);
|
||||
}
|
||||
err = snd_pcm_plug_playback_channels_mask(plug, srcmask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
if (!bitset_get(srcmask, channel))
|
||||
src_channels[channel].enabled = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *client_channels)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug);
|
||||
unsigned int nchannels = plugin->dst_format.channels;
|
||||
bitset_t bs[bitset_size(nchannels)];
|
||||
bitset_t *dstmask = bs;
|
||||
bitset_t *srcmask;
|
||||
int err;
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
if (client_channels[channel].enabled)
|
||||
bitset_set(dstmask, channel);
|
||||
else
|
||||
bitset_reset(dstmask, channel);
|
||||
}
|
||||
while (plugin) {
|
||||
err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dstmask = srcmask;
|
||||
plugin = plugin->prev;
|
||||
}
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
nchannels = plugin->src_format.channels;
|
||||
for (channel = 0; channel < nchannels; channel++) {
|
||||
if (!bitset_get(dstmask, channel))
|
||||
src_channels[channel].enabled = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin, *next;
|
||||
snd_pcm_plugin_channel_t *dst_channels;
|
||||
int err;
|
||||
snd_pcm_sframes_t frames = size;
|
||||
|
||||
if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0)
|
||||
return err;
|
||||
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
if ((next = plugin->next) != NULL) {
|
||||
snd_pcm_sframes_t frames1 = frames;
|
||||
if (plugin->dst_frames)
|
||||
frames1 = plugin->dst_frames(plugin, frames);
|
||||
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
|
||||
return err;
|
||||
}
|
||||
if (err != frames1) {
|
||||
frames = err;
|
||||
if (plugin->src_frames)
|
||||
frames = plugin->src_frames(plugin, frames1);
|
||||
}
|
||||
} else
|
||||
dst_channels = NULL;
|
||||
pdprintf("write plugin: %s, %li\n", plugin->name, frames);
|
||||
if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
|
||||
return frames;
|
||||
src_channels = dst_channels;
|
||||
plugin = next;
|
||||
}
|
||||
return snd_pcm_plug_client_size(plug, frames);
|
||||
}
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size)
|
||||
{
|
||||
snd_pcm_plugin_t *plugin, *next;
|
||||
snd_pcm_plugin_channel_t *src_channels, *dst_channels;
|
||||
snd_pcm_sframes_t frames = size;
|
||||
int err;
|
||||
|
||||
frames = snd_pcm_plug_slave_size(plug, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
|
||||
src_channels = NULL;
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
if ((next = plugin->next) != NULL) {
|
||||
if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
|
||||
return err;
|
||||
}
|
||||
frames = err;
|
||||
if (!plugin->prev) {
|
||||
if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
dst_channels = dst_channels_final;
|
||||
}
|
||||
pdprintf("read plugin: %s, %li\n", plugin->name, frames);
|
||||
if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
|
||||
return frames;
|
||||
plugin = next;
|
||||
src_channels = dst_channels;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
|
||||
size_t samples, int format)
|
||||
{
|
||||
/* FIXME: sub byte resolution and odd dst_offset */
|
||||
unsigned char *dst;
|
||||
unsigned int dst_step;
|
||||
int width;
|
||||
const unsigned char *silence;
|
||||
if (!dst_area->addr)
|
||||
return 0;
|
||||
dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
|
||||
width = snd_pcm_format_physical_width(format);
|
||||
if (width <= 0)
|
||||
return -EINVAL;
|
||||
if (dst_area->step == (unsigned int) width && width >= 8)
|
||||
return snd_pcm_format_set_silence(format, dst, samples);
|
||||
silence = snd_pcm_format_silence_64(format);
|
||||
if (! silence)
|
||||
return -EINVAL;
|
||||
dst_step = dst_area->step / 8;
|
||||
if (width == 4) {
|
||||
/* Ima ADPCM */
|
||||
int dstbit = dst_area->first % 8;
|
||||
int dstbit_step = dst_area->step % 8;
|
||||
while (samples-- > 0) {
|
||||
if (dstbit)
|
||||
*dst &= 0xf0;
|
||||
else
|
||||
*dst &= 0x0f;
|
||||
dst += dst_step;
|
||||
dstbit += dstbit_step;
|
||||
if (dstbit == 8) {
|
||||
dst++;
|
||||
dstbit = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
width /= 8;
|
||||
while (samples-- > 0) {
|
||||
memcpy(dst, silence, width);
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
|
||||
const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
|
||||
size_t samples, int format)
|
||||
{
|
||||
/* FIXME: sub byte resolution and odd dst_offset */
|
||||
char *src, *dst;
|
||||
int width;
|
||||
int src_step, dst_step;
|
||||
src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;
|
||||
if (!src_area->addr)
|
||||
return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
|
||||
dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;
|
||||
if (!dst_area->addr)
|
||||
return 0;
|
||||
width = snd_pcm_format_physical_width(format);
|
||||
if (width <= 0)
|
||||
return -EINVAL;
|
||||
if (src_area->step == (unsigned int) width &&
|
||||
dst_area->step == (unsigned int) width && width >= 8) {
|
||||
size_t bytes = samples * width / 8;
|
||||
memcpy(dst, src, bytes);
|
||||
return 0;
|
||||
}
|
||||
src_step = src_area->step / 8;
|
||||
dst_step = dst_area->step / 8;
|
||||
if (width == 4) {
|
||||
/* Ima ADPCM */
|
||||
int srcbit = src_area->first % 8;
|
||||
int srcbit_step = src_area->step % 8;
|
||||
int dstbit = dst_area->first % 8;
|
||||
int dstbit_step = dst_area->step % 8;
|
||||
while (samples-- > 0) {
|
||||
unsigned char srcval;
|
||||
if (srcbit)
|
||||
srcval = *src & 0x0f;
|
||||
else
|
||||
srcval = (*src & 0xf0) >> 4;
|
||||
if (dstbit)
|
||||
*dst = (*dst & 0xf0) | srcval;
|
||||
else
|
||||
*dst = (*dst & 0x0f) | (srcval << 4);
|
||||
src += src_step;
|
||||
srcbit += srcbit_step;
|
||||
if (srcbit == 8) {
|
||||
src++;
|
||||
srcbit = 0;
|
||||
}
|
||||
dst += dst_step;
|
||||
dstbit += dstbit_step;
|
||||
if (dstbit == 8) {
|
||||
dst++;
|
||||
dstbit = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
width /= 8;
|
||||
while (samples-- > 0) {
|
||||
memcpy(dst, src, width);
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
250
sound/core/oss/pcm_plugin.h
Normal file
250
sound/core/oss/pcm_plugin.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#ifndef __PCM_PLUGIN_H
|
||||
#define __PCM_PLUGIN_H
|
||||
|
||||
/*
|
||||
* Digital Audio (Plugin interface) abstract layer
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ATTRIBUTE_UNUSED
|
||||
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
typedef unsigned int bitset_t;
|
||||
|
||||
static inline size_t bitset_size(int nbits)
|
||||
{
|
||||
return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8);
|
||||
}
|
||||
|
||||
static inline bitset_t *bitset_alloc(int nbits)
|
||||
{
|
||||
return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void bitset_set(bitset_t *bitmap, unsigned int pos)
|
||||
{
|
||||
size_t bits = sizeof(*bitmap) * 8;
|
||||
bitmap[pos / bits] |= 1 << (pos % bits);
|
||||
}
|
||||
|
||||
static inline void bitset_reset(bitset_t *bitmap, unsigned int pos)
|
||||
{
|
||||
size_t bits = sizeof(*bitmap) * 8;
|
||||
bitmap[pos / bits] &= ~(1 << (pos % bits));
|
||||
}
|
||||
|
||||
static inline int bitset_get(bitset_t *bitmap, unsigned int pos)
|
||||
{
|
||||
size_t bits = sizeof(*bitmap) * 8;
|
||||
return !!(bitmap[pos / bits] & (1 << (pos % bits)));
|
||||
}
|
||||
|
||||
static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits)
|
||||
{
|
||||
memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t));
|
||||
}
|
||||
|
||||
static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits)
|
||||
{
|
||||
bitset_t *end = dst + bitset_size(nbits);
|
||||
while (dst < end)
|
||||
*dst++ &= *bs++;
|
||||
}
|
||||
|
||||
static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits)
|
||||
{
|
||||
bitset_t *end = dst + bitset_size(nbits);
|
||||
while (dst < end)
|
||||
*dst++ |= *bs++;
|
||||
}
|
||||
|
||||
static inline void bitset_zero(bitset_t *dst, unsigned int nbits)
|
||||
{
|
||||
bitset_t *end = dst + bitset_size(nbits);
|
||||
while (dst < end)
|
||||
*dst++ = 0;
|
||||
}
|
||||
|
||||
static inline void bitset_one(bitset_t *dst, unsigned int nbits)
|
||||
{
|
||||
bitset_t *end = dst + bitset_size(nbits);
|
||||
while (dst < end)
|
||||
*dst++ = ~(bitset_t)0;
|
||||
}
|
||||
|
||||
#define snd_pcm_plug_t snd_pcm_substream_t
|
||||
#define snd_pcm_plug_stream(plug) ((plug)->stream)
|
||||
|
||||
typedef enum {
|
||||
INIT = 0,
|
||||
PREPARE = 1,
|
||||
} snd_pcm_plugin_action_t;
|
||||
|
||||
typedef struct _snd_pcm_channel_area {
|
||||
void *addr; /* base address of channel samples */
|
||||
unsigned int first; /* offset to first sample in bits */
|
||||
unsigned int step; /* samples distance in bits */
|
||||
} snd_pcm_channel_area_t;
|
||||
|
||||
typedef struct _snd_pcm_plugin_channel {
|
||||
void *aptr; /* pointer to the allocated area */
|
||||
snd_pcm_channel_area_t area;
|
||||
snd_pcm_uframes_t frames; /* allocated frames */
|
||||
unsigned int enabled:1; /* channel need to be processed */
|
||||
unsigned int wanted:1; /* channel is wanted */
|
||||
} snd_pcm_plugin_channel_t;
|
||||
|
||||
typedef struct _snd_pcm_plugin_format {
|
||||
int format;
|
||||
unsigned int rate;
|
||||
unsigned int channels;
|
||||
} snd_pcm_plugin_format_t;
|
||||
|
||||
struct _snd_pcm_plugin {
|
||||
const char *name; /* plug-in name */
|
||||
int stream;
|
||||
snd_pcm_plugin_format_t src_format; /* source format */
|
||||
snd_pcm_plugin_format_t dst_format; /* destination format */
|
||||
int src_width; /* sample width in bits */
|
||||
int dst_width; /* sample width in bits */
|
||||
int access;
|
||||
snd_pcm_sframes_t (*src_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t dst_frames);
|
||||
snd_pcm_sframes_t (*dst_frames)(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t src_frames);
|
||||
snd_pcm_sframes_t (*client_channels)(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_uframes_t frames,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
int (*src_channels_mask)(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *dst_vmask,
|
||||
bitset_t **src_vmask);
|
||||
int (*dst_channels_mask)(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *src_vmask,
|
||||
bitset_t **dst_vmask);
|
||||
snd_pcm_sframes_t (*transfer)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames);
|
||||
int (*action)(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long data);
|
||||
snd_pcm_plugin_t *prev;
|
||||
snd_pcm_plugin_t *next;
|
||||
snd_pcm_plug_t *plug;
|
||||
void *private_data;
|
||||
void (*private_free)(snd_pcm_plugin_t *plugin);
|
||||
char *buf;
|
||||
snd_pcm_uframes_t buf_frames;
|
||||
snd_pcm_plugin_channel_t *buf_channels;
|
||||
bitset_t *src_vmask;
|
||||
bitset_t *dst_vmask;
|
||||
char extra_data[0];
|
||||
};
|
||||
|
||||
int snd_pcm_plugin_build(snd_pcm_plug_t *handle,
|
||||
const char *name,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
size_t extra,
|
||||
snd_pcm_plugin_t **ret);
|
||||
int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin);
|
||||
int snd_pcm_plugin_clear(snd_pcm_plugin_t **first);
|
||||
int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames);
|
||||
snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t drv_size);
|
||||
snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *handle, snd_pcm_uframes_t clt_size);
|
||||
|
||||
#define FULL ROUTE_PLUGIN_RESOLUTION
|
||||
#define HALF ROUTE_PLUGIN_RESOLUTION / 2
|
||||
typedef int route_ttable_entry_t;
|
||||
|
||||
int snd_pcm_plugin_build_io(snd_pcm_plug_t *handle,
|
||||
snd_pcm_hw_params_t *params,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_linear(snd_pcm_plug_t *handle,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *handle,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_rate(snd_pcm_plug_t *handle,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_route(snd_pcm_plug_t *handle,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
route_ttable_entry_t *ttable,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
int snd_pcm_plugin_build_copy(snd_pcm_plug_t *handle,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin);
|
||||
|
||||
int snd_pcm_plug_format_plugins(snd_pcm_plug_t *substream,
|
||||
snd_pcm_hw_params_t *params,
|
||||
snd_pcm_hw_params_t *slave_params);
|
||||
|
||||
int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask);
|
||||
|
||||
int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin);
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size);
|
||||
snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *handle, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size);
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *handle,
|
||||
char *buf, snd_pcm_uframes_t count,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
|
||||
snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_uframes_t frames,
|
||||
snd_pcm_plugin_channel_t **channels);
|
||||
|
||||
int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
size_t samples, int format);
|
||||
int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset,
|
||||
const snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
|
||||
size_t samples, int format);
|
||||
|
||||
void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t size);
|
||||
void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *plug, void *ptr);
|
||||
snd_pcm_sframes_t snd_pcm_oss_write3(snd_pcm_substream_t *substream, const char *ptr, snd_pcm_uframes_t size, int in_kernel);
|
||||
snd_pcm_sframes_t snd_pcm_oss_read3(snd_pcm_substream_t *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel);
|
||||
snd_pcm_sframes_t snd_pcm_oss_writev3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
|
||||
snd_pcm_sframes_t snd_pcm_oss_readv3(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel);
|
||||
|
||||
|
||||
|
||||
#define ROUTE_PLUGIN_RESOLUTION 16
|
||||
|
||||
int getput_index(int format);
|
||||
int copy_index(int format);
|
||||
int conv_index(int src_format, int dst_format);
|
||||
|
||||
void zero_channel(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *dst_channel,
|
||||
size_t samples);
|
||||
|
||||
#ifdef PLUGIN_DEBUG
|
||||
#define pdprintf( fmt, args... ) printk( "plugin: " fmt, ##args)
|
||||
#else
|
||||
#define pdprintf( fmt, args... )
|
||||
#endif
|
||||
|
||||
#endif /* __PCM_PLUGIN_H */
|
||||
536
sound/core/oss/plugin_ops.h
Normal file
536
sound/core/oss/plugin_ops.h
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Plugin sample operators with fast switch
|
||||
* Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#define as_u8(ptr) (*(u_int8_t*)(ptr))
|
||||
#define as_u16(ptr) (*(u_int16_t*)(ptr))
|
||||
#define as_u32(ptr) (*(u_int32_t*)(ptr))
|
||||
#define as_u64(ptr) (*(u_int64_t*)(ptr))
|
||||
#define as_s8(ptr) (*(int8_t*)(ptr))
|
||||
#define as_s16(ptr) (*(int16_t*)(ptr))
|
||||
#define as_s32(ptr) (*(int32_t*)(ptr))
|
||||
#define as_s64(ptr) (*(int64_t*)(ptr))
|
||||
|
||||
#ifdef COPY_LABELS
|
||||
static void *copy_labels[4] = {
|
||||
&©_8,
|
||||
&©_16,
|
||||
&©_32,
|
||||
&©_64
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef COPY_END
|
||||
while(0) {
|
||||
copy_8: as_s8(dst) = as_s8(src); goto COPY_END;
|
||||
copy_16: as_s16(dst) = as_s16(src); goto COPY_END;
|
||||
copy_32: as_s32(dst) = as_s32(src); goto COPY_END;
|
||||
copy_64: as_s64(dst) = as_s64(src); goto COPY_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONV_LABELS
|
||||
/* src_wid src_endswap sign_toggle dst_wid dst_endswap */
|
||||
static void *conv_labels[4 * 2 * 2 * 4 * 2] = {
|
||||
&&conv_xxx1_xxx1, /* 8h -> 8h */
|
||||
&&conv_xxx1_xxx1, /* 8h -> 8s */
|
||||
&&conv_xxx1_xx10, /* 8h -> 16h */
|
||||
&&conv_xxx1_xx01, /* 8h -> 16s */
|
||||
&&conv_xxx1_x100, /* 8h -> 24h */
|
||||
&&conv_xxx1_001x, /* 8h -> 24s */
|
||||
&&conv_xxx1_1000, /* 8h -> 32h */
|
||||
&&conv_xxx1_0001, /* 8h -> 32s */
|
||||
&&conv_xxx1_xxx9, /* 8h ^> 8h */
|
||||
&&conv_xxx1_xxx9, /* 8h ^> 8s */
|
||||
&&conv_xxx1_xx90, /* 8h ^> 16h */
|
||||
&&conv_xxx1_xx09, /* 8h ^> 16s */
|
||||
&&conv_xxx1_x900, /* 8h ^> 24h */
|
||||
&&conv_xxx1_009x, /* 8h ^> 24s */
|
||||
&&conv_xxx1_9000, /* 8h ^> 32h */
|
||||
&&conv_xxx1_0009, /* 8h ^> 32s */
|
||||
&&conv_xxx1_xxx1, /* 8s -> 8h */
|
||||
&&conv_xxx1_xxx1, /* 8s -> 8s */
|
||||
&&conv_xxx1_xx10, /* 8s -> 16h */
|
||||
&&conv_xxx1_xx01, /* 8s -> 16s */
|
||||
&&conv_xxx1_x100, /* 8s -> 24h */
|
||||
&&conv_xxx1_001x, /* 8s -> 24s */
|
||||
&&conv_xxx1_1000, /* 8s -> 32h */
|
||||
&&conv_xxx1_0001, /* 8s -> 32s */
|
||||
&&conv_xxx1_xxx9, /* 8s ^> 8h */
|
||||
&&conv_xxx1_xxx9, /* 8s ^> 8s */
|
||||
&&conv_xxx1_xx90, /* 8s ^> 16h */
|
||||
&&conv_xxx1_xx09, /* 8s ^> 16s */
|
||||
&&conv_xxx1_x900, /* 8s ^> 24h */
|
||||
&&conv_xxx1_009x, /* 8s ^> 24s */
|
||||
&&conv_xxx1_9000, /* 8s ^> 32h */
|
||||
&&conv_xxx1_0009, /* 8s ^> 32s */
|
||||
&&conv_xx12_xxx1, /* 16h -> 8h */
|
||||
&&conv_xx12_xxx1, /* 16h -> 8s */
|
||||
&&conv_xx12_xx12, /* 16h -> 16h */
|
||||
&&conv_xx12_xx21, /* 16h -> 16s */
|
||||
&&conv_xx12_x120, /* 16h -> 24h */
|
||||
&&conv_xx12_021x, /* 16h -> 24s */
|
||||
&&conv_xx12_1200, /* 16h -> 32h */
|
||||
&&conv_xx12_0021, /* 16h -> 32s */
|
||||
&&conv_xx12_xxx9, /* 16h ^> 8h */
|
||||
&&conv_xx12_xxx9, /* 16h ^> 8s */
|
||||
&&conv_xx12_xx92, /* 16h ^> 16h */
|
||||
&&conv_xx12_xx29, /* 16h ^> 16s */
|
||||
&&conv_xx12_x920, /* 16h ^> 24h */
|
||||
&&conv_xx12_029x, /* 16h ^> 24s */
|
||||
&&conv_xx12_9200, /* 16h ^> 32h */
|
||||
&&conv_xx12_0029, /* 16h ^> 32s */
|
||||
&&conv_xx12_xxx2, /* 16s -> 8h */
|
||||
&&conv_xx12_xxx2, /* 16s -> 8s */
|
||||
&&conv_xx12_xx21, /* 16s -> 16h */
|
||||
&&conv_xx12_xx12, /* 16s -> 16s */
|
||||
&&conv_xx12_x210, /* 16s -> 24h */
|
||||
&&conv_xx12_012x, /* 16s -> 24s */
|
||||
&&conv_xx12_2100, /* 16s -> 32h */
|
||||
&&conv_xx12_0012, /* 16s -> 32s */
|
||||
&&conv_xx12_xxxA, /* 16s ^> 8h */
|
||||
&&conv_xx12_xxxA, /* 16s ^> 8s */
|
||||
&&conv_xx12_xxA1, /* 16s ^> 16h */
|
||||
&&conv_xx12_xx1A, /* 16s ^> 16s */
|
||||
&&conv_xx12_xA10, /* 16s ^> 24h */
|
||||
&&conv_xx12_01Ax, /* 16s ^> 24s */
|
||||
&&conv_xx12_A100, /* 16s ^> 32h */
|
||||
&&conv_xx12_001A, /* 16s ^> 32s */
|
||||
&&conv_x123_xxx1, /* 24h -> 8h */
|
||||
&&conv_x123_xxx1, /* 24h -> 8s */
|
||||
&&conv_x123_xx12, /* 24h -> 16h */
|
||||
&&conv_x123_xx21, /* 24h -> 16s */
|
||||
&&conv_x123_x123, /* 24h -> 24h */
|
||||
&&conv_x123_321x, /* 24h -> 24s */
|
||||
&&conv_x123_1230, /* 24h -> 32h */
|
||||
&&conv_x123_0321, /* 24h -> 32s */
|
||||
&&conv_x123_xxx9, /* 24h ^> 8h */
|
||||
&&conv_x123_xxx9, /* 24h ^> 8s */
|
||||
&&conv_x123_xx92, /* 24h ^> 16h */
|
||||
&&conv_x123_xx29, /* 24h ^> 16s */
|
||||
&&conv_x123_x923, /* 24h ^> 24h */
|
||||
&&conv_x123_329x, /* 24h ^> 24s */
|
||||
&&conv_x123_9230, /* 24h ^> 32h */
|
||||
&&conv_x123_0329, /* 24h ^> 32s */
|
||||
&&conv_123x_xxx3, /* 24s -> 8h */
|
||||
&&conv_123x_xxx3, /* 24s -> 8s */
|
||||
&&conv_123x_xx32, /* 24s -> 16h */
|
||||
&&conv_123x_xx23, /* 24s -> 16s */
|
||||
&&conv_123x_x321, /* 24s -> 24h */
|
||||
&&conv_123x_123x, /* 24s -> 24s */
|
||||
&&conv_123x_3210, /* 24s -> 32h */
|
||||
&&conv_123x_0123, /* 24s -> 32s */
|
||||
&&conv_123x_xxxB, /* 24s ^> 8h */
|
||||
&&conv_123x_xxxB, /* 24s ^> 8s */
|
||||
&&conv_123x_xxB2, /* 24s ^> 16h */
|
||||
&&conv_123x_xx2B, /* 24s ^> 16s */
|
||||
&&conv_123x_xB21, /* 24s ^> 24h */
|
||||
&&conv_123x_12Bx, /* 24s ^> 24s */
|
||||
&&conv_123x_B210, /* 24s ^> 32h */
|
||||
&&conv_123x_012B, /* 24s ^> 32s */
|
||||
&&conv_1234_xxx1, /* 32h -> 8h */
|
||||
&&conv_1234_xxx1, /* 32h -> 8s */
|
||||
&&conv_1234_xx12, /* 32h -> 16h */
|
||||
&&conv_1234_xx21, /* 32h -> 16s */
|
||||
&&conv_1234_x123, /* 32h -> 24h */
|
||||
&&conv_1234_321x, /* 32h -> 24s */
|
||||
&&conv_1234_1234, /* 32h -> 32h */
|
||||
&&conv_1234_4321, /* 32h -> 32s */
|
||||
&&conv_1234_xxx9, /* 32h ^> 8h */
|
||||
&&conv_1234_xxx9, /* 32h ^> 8s */
|
||||
&&conv_1234_xx92, /* 32h ^> 16h */
|
||||
&&conv_1234_xx29, /* 32h ^> 16s */
|
||||
&&conv_1234_x923, /* 32h ^> 24h */
|
||||
&&conv_1234_329x, /* 32h ^> 24s */
|
||||
&&conv_1234_9234, /* 32h ^> 32h */
|
||||
&&conv_1234_4329, /* 32h ^> 32s */
|
||||
&&conv_1234_xxx4, /* 32s -> 8h */
|
||||
&&conv_1234_xxx4, /* 32s -> 8s */
|
||||
&&conv_1234_xx43, /* 32s -> 16h */
|
||||
&&conv_1234_xx34, /* 32s -> 16s */
|
||||
&&conv_1234_x432, /* 32s -> 24h */
|
||||
&&conv_1234_234x, /* 32s -> 24s */
|
||||
&&conv_1234_4321, /* 32s -> 32h */
|
||||
&&conv_1234_1234, /* 32s -> 32s */
|
||||
&&conv_1234_xxxC, /* 32s ^> 8h */
|
||||
&&conv_1234_xxxC, /* 32s ^> 8s */
|
||||
&&conv_1234_xxC3, /* 32s ^> 16h */
|
||||
&&conv_1234_xx3C, /* 32s ^> 16s */
|
||||
&&conv_1234_xC32, /* 32s ^> 24h */
|
||||
&&conv_1234_23Cx, /* 32s ^> 24s */
|
||||
&&conv_1234_C321, /* 32s ^> 32h */
|
||||
&&conv_1234_123C, /* 32s ^> 32s */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONV_END
|
||||
while(0) {
|
||||
conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END;
|
||||
conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END;
|
||||
conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END;
|
||||
conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END;
|
||||
conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END;
|
||||
conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END;
|
||||
conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END;
|
||||
conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END;
|
||||
conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END;
|
||||
conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END;
|
||||
conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END;
|
||||
conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END;
|
||||
conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END;
|
||||
conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END;
|
||||
conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END;
|
||||
conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
|
||||
conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
|
||||
conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END;
|
||||
conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END;
|
||||
conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END;
|
||||
conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END;
|
||||
conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END;
|
||||
conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END;
|
||||
conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END;
|
||||
conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END;
|
||||
conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END;
|
||||
conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END;
|
||||
conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END;
|
||||
conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END;
|
||||
conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END;
|
||||
conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END;
|
||||
conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END;
|
||||
conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END;
|
||||
conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END;
|
||||
conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END;
|
||||
conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END;
|
||||
conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END;
|
||||
conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
|
||||
conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END;
|
||||
conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
|
||||
conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
|
||||
conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
|
||||
conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END;
|
||||
conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END;
|
||||
conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END;
|
||||
conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END;
|
||||
conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END;
|
||||
conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END;
|
||||
conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END;
|
||||
conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END;
|
||||
conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END;
|
||||
conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END;
|
||||
conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
|
||||
conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END;
|
||||
conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
|
||||
conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
|
||||
conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END;
|
||||
conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END;
|
||||
conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END;
|
||||
conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END;
|
||||
conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END;
|
||||
conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END;
|
||||
conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END;
|
||||
conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END;
|
||||
conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END;
|
||||
conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END;
|
||||
conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END;
|
||||
conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END;
|
||||
conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END;
|
||||
conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END;
|
||||
conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END;
|
||||
conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END;
|
||||
conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END;
|
||||
conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END;
|
||||
conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END;
|
||||
conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END;
|
||||
conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END;
|
||||
conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END;
|
||||
conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END;
|
||||
conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END;
|
||||
conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END;
|
||||
conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END;
|
||||
conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END;
|
||||
conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END;
|
||||
conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END;
|
||||
conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END;
|
||||
conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END;
|
||||
conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GET_S16_LABELS
|
||||
/* src_wid src_endswap unsigned */
|
||||
static void *get_s16_labels[4 * 2 * 2] = {
|
||||
&&get_s16_xxx1_xx10, /* 8h -> 16h */
|
||||
&&get_s16_xxx1_xx90, /* 8h ^> 16h */
|
||||
&&get_s16_xxx1_xx10, /* 8s -> 16h */
|
||||
&&get_s16_xxx1_xx90, /* 8s ^> 16h */
|
||||
&&get_s16_xx12_xx12, /* 16h -> 16h */
|
||||
&&get_s16_xx12_xx92, /* 16h ^> 16h */
|
||||
&&get_s16_xx12_xx21, /* 16s -> 16h */
|
||||
&&get_s16_xx12_xxA1, /* 16s ^> 16h */
|
||||
&&get_s16_x123_xx12, /* 24h -> 16h */
|
||||
&&get_s16_x123_xx92, /* 24h ^> 16h */
|
||||
&&get_s16_123x_xx32, /* 24s -> 16h */
|
||||
&&get_s16_123x_xxB2, /* 24s ^> 16h */
|
||||
&&get_s16_1234_xx12, /* 32h -> 16h */
|
||||
&&get_s16_1234_xx92, /* 32h ^> 16h */
|
||||
&&get_s16_1234_xx43, /* 32s -> 16h */
|
||||
&&get_s16_1234_xxC3, /* 32s ^> 16h */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef GET_S16_END
|
||||
while(0) {
|
||||
get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END;
|
||||
get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END;
|
||||
get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END;
|
||||
get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END;
|
||||
get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END;
|
||||
get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END;
|
||||
get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END;
|
||||
get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END;
|
||||
get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END;
|
||||
get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END;
|
||||
get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END;
|
||||
get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END;
|
||||
get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END;
|
||||
get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PUT_S16_LABELS
|
||||
/* dst_wid dst_endswap unsigned */
|
||||
static void *put_s16_labels[4 * 2 * 2] = {
|
||||
&&put_s16_xx12_xxx1, /* 16h -> 8h */
|
||||
&&put_s16_xx12_xxx9, /* 16h ^> 8h */
|
||||
&&put_s16_xx12_xxx1, /* 16h -> 8s */
|
||||
&&put_s16_xx12_xxx9, /* 16h ^> 8s */
|
||||
&&put_s16_xx12_xx12, /* 16h -> 16h */
|
||||
&&put_s16_xx12_xx92, /* 16h ^> 16h */
|
||||
&&put_s16_xx12_xx21, /* 16h -> 16s */
|
||||
&&put_s16_xx12_xx29, /* 16h ^> 16s */
|
||||
&&put_s16_xx12_x120, /* 16h -> 24h */
|
||||
&&put_s16_xx12_x920, /* 16h ^> 24h */
|
||||
&&put_s16_xx12_021x, /* 16h -> 24s */
|
||||
&&put_s16_xx12_029x, /* 16h ^> 24s */
|
||||
&&put_s16_xx12_1200, /* 16h -> 32h */
|
||||
&&put_s16_xx12_9200, /* 16h ^> 32h */
|
||||
&&put_s16_xx12_0021, /* 16h -> 32s */
|
||||
&&put_s16_xx12_0029, /* 16h ^> 32s */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef PUT_S16_END
|
||||
while (0) {
|
||||
put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END;
|
||||
put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END;
|
||||
put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END;
|
||||
put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END;
|
||||
put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END;
|
||||
put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END;
|
||||
put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END;
|
||||
put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END;
|
||||
put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END;
|
||||
put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END;
|
||||
put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END;
|
||||
put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END;
|
||||
put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END;
|
||||
put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef GET32_LABELS
|
||||
/* src_wid src_endswap unsigned */
|
||||
static void *get32_labels[4 * 2 * 2] = {
|
||||
&&get32_xxx1_1000, /* 8h -> 32h */
|
||||
&&get32_xxx1_9000, /* 8h ^> 32h */
|
||||
&&get32_xxx1_1000, /* 8s -> 32h */
|
||||
&&get32_xxx1_9000, /* 8s ^> 32h */
|
||||
&&get32_xx12_1200, /* 16h -> 32h */
|
||||
&&get32_xx12_9200, /* 16h ^> 32h */
|
||||
&&get32_xx12_2100, /* 16s -> 32h */
|
||||
&&get32_xx12_A100, /* 16s ^> 32h */
|
||||
&&get32_x123_1230, /* 24h -> 32h */
|
||||
&&get32_x123_9230, /* 24h ^> 32h */
|
||||
&&get32_123x_3210, /* 24s -> 32h */
|
||||
&&get32_123x_B210, /* 24s ^> 32h */
|
||||
&&get32_1234_1234, /* 32h -> 32h */
|
||||
&&get32_1234_9234, /* 32h ^> 32h */
|
||||
&&get32_1234_4321, /* 32s -> 32h */
|
||||
&&get32_1234_C321, /* 32s ^> 32h */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef GET32_END
|
||||
while (0) {
|
||||
get32_xxx1_1000: sample = (u_int32_t)as_u8(src) << 24; goto GET32_END;
|
||||
get32_xxx1_9000: sample = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto GET32_END;
|
||||
get32_xx12_1200: sample = (u_int32_t)as_u16(src) << 16; goto GET32_END;
|
||||
get32_xx12_9200: sample = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto GET32_END;
|
||||
get32_xx12_2100: sample = (u_int32_t)swab16(as_u16(src)) << 16; goto GET32_END;
|
||||
get32_xx12_A100: sample = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto GET32_END;
|
||||
get32_x123_1230: sample = as_u32(src) << 8; goto GET32_END;
|
||||
get32_x123_9230: sample = (as_u32(src) << 8) ^ 0x80000000; goto GET32_END;
|
||||
get32_123x_3210: sample = swab32(as_u32(src) >> 8); goto GET32_END;
|
||||
get32_123x_B210: sample = swab32((as_u32(src) >> 8) ^ 0x80); goto GET32_END;
|
||||
get32_1234_1234: sample = as_u32(src); goto GET32_END;
|
||||
get32_1234_9234: sample = as_u32(src) ^ 0x80000000; goto GET32_END;
|
||||
get32_1234_4321: sample = swab32(as_u32(src)); goto GET32_END;
|
||||
get32_1234_C321: sample = swab32(as_u32(src) ^ 0x80); goto GET32_END;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PUT_U32_LABELS
|
||||
/* dst_wid dst_endswap unsigned */
|
||||
static void *put_u32_labels[4 * 2 * 2] = {
|
||||
&&put_u32_1234_xxx9, /* u32h -> s8h */
|
||||
&&put_u32_1234_xxx1, /* u32h -> u8h */
|
||||
&&put_u32_1234_xxx9, /* u32h -> s8s */
|
||||
&&put_u32_1234_xxx1, /* u32h -> u8s */
|
||||
&&put_u32_1234_xx92, /* u32h -> s16h */
|
||||
&&put_u32_1234_xx12, /* u32h -> u16h */
|
||||
&&put_u32_1234_xx29, /* u32h -> s16s */
|
||||
&&put_u32_1234_xx21, /* u32h -> u16s */
|
||||
&&put_u32_1234_x923, /* u32h -> s24h */
|
||||
&&put_u32_1234_x123, /* u32h -> u24h */
|
||||
&&put_u32_1234_329x, /* u32h -> s24s */
|
||||
&&put_u32_1234_321x, /* u32h -> u24s */
|
||||
&&put_u32_1234_9234, /* u32h -> s32h */
|
||||
&&put_u32_1234_1234, /* u32h -> u32h */
|
||||
&&put_u32_1234_4329, /* u32h -> s32s */
|
||||
&&put_u32_1234_4321, /* u32h -> u32s */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef PUT_U32_END
|
||||
while (0) {
|
||||
put_u32_1234_xxx1: as_u8(dst) = sample >> 24; goto PUT_U32_END;
|
||||
put_u32_1234_xxx9: as_u8(dst) = (sample >> 24) ^ 0x80; goto PUT_U32_END;
|
||||
put_u32_1234_xx12: as_u16(dst) = sample >> 16; goto PUT_U32_END;
|
||||
put_u32_1234_xx92: as_u16(dst) = (sample >> 16) ^ 0x8000; goto PUT_U32_END;
|
||||
put_u32_1234_xx21: as_u16(dst) = swab16(sample >> 16); goto PUT_U32_END;
|
||||
put_u32_1234_xx29: as_u16(dst) = swab16(sample >> 16) ^ 0x80; goto PUT_U32_END;
|
||||
put_u32_1234_x123: as_u32(dst) = sample >> 8; goto PUT_U32_END;
|
||||
put_u32_1234_x923: as_u32(dst) = (sample >> 8) ^ 0x800000; goto PUT_U32_END;
|
||||
put_u32_1234_321x: as_u32(dst) = swab32(sample) << 8; goto PUT_U32_END;
|
||||
put_u32_1234_329x: as_u32(dst) = (swab32(sample) ^ 0x80) << 8; goto PUT_U32_END;
|
||||
put_u32_1234_1234: as_u32(dst) = sample; goto PUT_U32_END;
|
||||
put_u32_1234_9234: as_u32(dst) = sample ^ 0x80000000; goto PUT_U32_END;
|
||||
put_u32_1234_4321: as_u32(dst) = swab32(sample); goto PUT_U32_END;
|
||||
put_u32_1234_4329: as_u32(dst) = swab32(sample) ^ 0x80; goto PUT_U32_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GET_U_LABELS
|
||||
/* width endswap unsigned*/
|
||||
static void *get_u_labels[4 * 2 * 2] = {
|
||||
&&get_u_s8, /* s8 -> u8 */
|
||||
&&get_u_u8, /* u8 -> u8 */
|
||||
&&get_u_s8, /* s8 -> u8 */
|
||||
&&get_u_u8, /* u8 -> u8 */
|
||||
&&get_u_s16h, /* s16h -> u16h */
|
||||
&&get_u_u16h, /* u16h -> u16h */
|
||||
&&get_u_s16s, /* s16s -> u16h */
|
||||
&&get_u_u16s, /* u16s -> u16h */
|
||||
&&get_u_s24h, /* s24h -> u32h */
|
||||
&&get_u_u24h, /* u24h -> u32h */
|
||||
&&get_u_s24s, /* s24s -> u32h */
|
||||
&&get_u_u24s, /* u24s -> u32h */
|
||||
&&get_u_s32h, /* s32h -> u32h */
|
||||
&&get_u_u32h, /* u32h -> u32h */
|
||||
&&get_u_s32s, /* s32s -> u32h */
|
||||
&&get_u_u32s, /* u32s -> u32h */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef GET_U_END
|
||||
while (0) {
|
||||
get_u_s8: sample = as_u8(src) ^ 0x80; goto GET_U_END;
|
||||
get_u_u8: sample = as_u8(src); goto GET_U_END;
|
||||
get_u_s16h: sample = as_u16(src) ^ 0x8000; goto GET_U_END;
|
||||
get_u_u16h: sample = as_u16(src); goto GET_U_END;
|
||||
get_u_s16s: sample = swab16(as_u16(src) ^ 0x80); goto GET_U_END;
|
||||
get_u_u16s: sample = swab16(as_u16(src)); goto GET_U_END;
|
||||
get_u_s24h: sample = (as_u32(src) ^ 0x800000); goto GET_U_END;
|
||||
get_u_u24h: sample = as_u32(src); goto GET_U_END;
|
||||
get_u_s24s: sample = swab32(as_u32(src) ^ 0x800000); goto GET_U_END;
|
||||
get_u_u24s: sample = swab32(as_u32(src)); goto GET_U_END;
|
||||
get_u_s32h: sample = as_u32(src) ^ 0x80000000; goto GET_U_END;
|
||||
get_u_u32h: sample = as_u32(src); goto GET_U_END;
|
||||
get_u_s32s: sample = swab32(as_u32(src) ^ 0x80); goto GET_U_END;
|
||||
get_u_u32s: sample = swab32(as_u32(src)); goto GET_U_END;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef PUT_LABELS
|
||||
/* width endswap unsigned */
|
||||
static void *put_labels[4 * 2 * 2] = {
|
||||
&&put_s8, /* s8 -> s8 */
|
||||
&&put_u8, /* u8 -> s8 */
|
||||
&&put_s8, /* s8 -> s8 */
|
||||
&&put_u8, /* u8 -> s8 */
|
||||
&&put_s16h, /* s16h -> s16h */
|
||||
&&put_u16h, /* u16h -> s16h */
|
||||
&&put_s16s, /* s16s -> s16h */
|
||||
&&put_u16s, /* u16s -> s16h */
|
||||
&&put_s24h, /* s24h -> s32h */
|
||||
&&put_u24h, /* u24h -> s32h */
|
||||
&&put_s24s, /* s24s -> s32h */
|
||||
&&put_u24s, /* u24s -> s32h */
|
||||
&&put_s32h, /* s32h -> s32h */
|
||||
&&put_u32h, /* u32h -> s32h */
|
||||
&&put_s32s, /* s32s -> s32h */
|
||||
&&put_u32s, /* u32s -> s32h */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef PUT_END
|
||||
put_s8: as_s8(dst) = sample; goto PUT_END;
|
||||
put_u8: as_u8(dst) = sample ^ 0x80; goto PUT_END;
|
||||
put_s16h: as_s16(dst) = sample; goto PUT_END;
|
||||
put_u16h: as_u16(dst) = sample ^ 0x8000; goto PUT_END;
|
||||
put_s16s: as_s16(dst) = swab16(sample); goto PUT_END;
|
||||
put_u16s: as_u16(dst) = swab16(sample ^ 0x80); goto PUT_END;
|
||||
put_s24h: as_s24(dst) = sample & 0xffffff; goto PUT_END;
|
||||
put_u24h: as_u24(dst) = sample ^ 0x80000000; goto PUT_END;
|
||||
put_s24s: as_s24(dst) = swab32(sample & 0xffffff); goto PUT_END;
|
||||
put_u24s: as_u24(dst) = swab32(sample ^ 0x80); goto PUT_END;
|
||||
put_s32h: as_s32(dst) = sample; goto PUT_END;
|
||||
put_u32h: as_u32(dst) = sample ^ 0x80000000; goto PUT_END;
|
||||
put_s32s: as_s32(dst) = swab32(sample); goto PUT_END;
|
||||
put_u32s: as_u32(dst) = swab32(sample ^ 0x80); goto PUT_END;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef as_u8
|
||||
#undef as_u16
|
||||
#undef as_u32
|
||||
#undef as_s8
|
||||
#undef as_s16
|
||||
#undef as_s32
|
||||
378
sound/core/oss/rate.c
Normal file
378
sound/core/oss/rate.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Rate conversion Plug-In
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
#define SHIFT 11
|
||||
#define BITS (1<<SHIFT)
|
||||
#define R_MASK (BITS-1)
|
||||
|
||||
/*
|
||||
* Basic rate conversion plugin
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
signed short last_S1;
|
||||
signed short last_S2;
|
||||
} rate_channel_t;
|
||||
|
||||
typedef void (*rate_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames);
|
||||
|
||||
typedef struct rate_private_data {
|
||||
unsigned int pitch;
|
||||
unsigned int pos;
|
||||
rate_f func;
|
||||
int get, put;
|
||||
snd_pcm_sframes_t old_src_frames, old_dst_frames;
|
||||
rate_channel_t channels[0];
|
||||
} rate_t;
|
||||
|
||||
static void rate_init(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
unsigned int channel;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
data->pos = 0;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
data->channels[channel].last_S1 = 0;
|
||||
data->channels[channel].last_S2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void resample_expand(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
char *src, *dst;
|
||||
unsigned int channel;
|
||||
int src_step, dst_step;
|
||||
int src_frames1, dst_frames1;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
rate_channel_t *rchannels = data->channels;
|
||||
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[data->get];
|
||||
void *put = put_s16_labels[data->put];
|
||||
signed short sample = 0;
|
||||
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
pos = data->pos;
|
||||
S1 = rchannels->last_S1;
|
||||
S2 = rchannels->last_S2;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
src_frames1 = src_frames;
|
||||
dst_frames1 = dst_frames;
|
||||
while (dst_frames1-- > 0) {
|
||||
if (pos & ~R_MASK) {
|
||||
pos &= R_MASK;
|
||||
S1 = S2;
|
||||
if (src_frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after_get:
|
||||
S2 = sample;
|
||||
src += src_step;
|
||||
}
|
||||
}
|
||||
val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
|
||||
if (val < -32768)
|
||||
val = -32768;
|
||||
else if (val > 32767)
|
||||
val = 32767;
|
||||
sample = val;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
pos += data->pitch;
|
||||
}
|
||||
rchannels->last_S1 = S1;
|
||||
rchannels->last_S2 = S2;
|
||||
rchannels++;
|
||||
}
|
||||
data->pos = pos;
|
||||
}
|
||||
|
||||
static void resample_shrink(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
int src_frames, int dst_frames)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
signed int val;
|
||||
signed short S1, S2;
|
||||
char *src, *dst;
|
||||
unsigned int channel;
|
||||
int src_step, dst_step;
|
||||
int src_frames1, dst_frames1;
|
||||
rate_t *data = (rate_t *)plugin->extra_data;
|
||||
rate_channel_t *rchannels = data->channels;
|
||||
|
||||
#define GET_S16_LABELS
|
||||
#define PUT_S16_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_LABELS
|
||||
#undef PUT_S16_LABELS
|
||||
void *get = get_s16_labels[data->get];
|
||||
void *put = put_s16_labels[data->put];
|
||||
signed short sample = 0;
|
||||
|
||||
for (channel = 0; channel < plugin->src_format.channels; ++channel) {
|
||||
pos = data->pos;
|
||||
S1 = rchannels->last_S1;
|
||||
S2 = rchannels->last_S2;
|
||||
if (!src_channels[channel].enabled) {
|
||||
if (dst_channels[channel].wanted)
|
||||
snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
|
||||
dst_channels[channel].enabled = 0;
|
||||
continue;
|
||||
}
|
||||
dst_channels[channel].enabled = 1;
|
||||
src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8;
|
||||
dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
|
||||
src_step = src_channels[channel].area.step / 8;
|
||||
dst_step = dst_channels[channel].area.step / 8;
|
||||
src_frames1 = src_frames;
|
||||
dst_frames1 = dst_frames;
|
||||
while (dst_frames1 > 0) {
|
||||
S1 = S2;
|
||||
if (src_frames1-- > 0) {
|
||||
goto *get;
|
||||
#define GET_S16_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_S16_END
|
||||
after_get:
|
||||
S2 = sample;
|
||||
src += src_step;
|
||||
}
|
||||
if (pos & ~R_MASK) {
|
||||
pos &= R_MASK;
|
||||
val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
|
||||
if (val < -32768)
|
||||
val = -32768;
|
||||
else if (val > 32767)
|
||||
val = 32767;
|
||||
sample = val;
|
||||
goto *put;
|
||||
#define PUT_S16_END after_put
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_S16_END
|
||||
after_put:
|
||||
dst += dst_step;
|
||||
dst_frames1--;
|
||||
}
|
||||
pos += data->pitch;
|
||||
}
|
||||
rchannels->last_S1 = S1;
|
||||
rchannels->last_S2 = S2;
|
||||
rchannels++;
|
||||
}
|
||||
data->pos = pos;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t rate_src_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
|
||||
{
|
||||
rate_t *data;
|
||||
snd_pcm_sframes_t res;
|
||||
|
||||
snd_assert(plugin != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
if (plugin->src_format.rate < plugin->dst_format.rate) {
|
||||
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
|
||||
} else {
|
||||
res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
|
||||
}
|
||||
if (data->old_src_frames > 0) {
|
||||
snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
|
||||
while (data->old_src_frames < frames1) {
|
||||
frames1 >>= 1;
|
||||
res1 <<= 1;
|
||||
}
|
||||
while (data->old_src_frames > frames1) {
|
||||
frames1 <<= 1;
|
||||
res1 >>= 1;
|
||||
}
|
||||
if (data->old_src_frames == frames1)
|
||||
return res1;
|
||||
}
|
||||
data->old_src_frames = frames;
|
||||
data->old_dst_frames = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t rate_dst_frames(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
|
||||
{
|
||||
rate_t *data;
|
||||
snd_pcm_sframes_t res;
|
||||
|
||||
snd_assert(plugin != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
if (plugin->src_format.rate < plugin->dst_format.rate) {
|
||||
res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
|
||||
} else {
|
||||
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
|
||||
}
|
||||
if (data->old_dst_frames > 0) {
|
||||
snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
|
||||
while (data->old_dst_frames < frames1) {
|
||||
frames1 >>= 1;
|
||||
res1 <<= 1;
|
||||
}
|
||||
while (data->old_dst_frames > frames1) {
|
||||
frames1 <<= 1;
|
||||
res1 >>= 1;
|
||||
}
|
||||
if (data->old_dst_frames == frames1)
|
||||
return res1;
|
||||
}
|
||||
data->old_dst_frames = frames;
|
||||
data->old_src_frames = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t rate_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
snd_pcm_uframes_t dst_frames;
|
||||
rate_t *data;
|
||||
|
||||
snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
{
|
||||
unsigned int channel;
|
||||
for (channel = 0; channel < plugin->src_format.channels; channel++) {
|
||||
snd_assert(src_channels[channel].area.first % 8 == 0 &&
|
||||
src_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
snd_assert(dst_channels[channel].area.first % 8 == 0 &&
|
||||
dst_channels[channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dst_frames = rate_dst_frames(plugin, frames);
|
||||
if (dst_frames > dst_channels[0].frames)
|
||||
dst_frames = dst_channels[0].frames;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
data->func(plugin, src_channels, dst_channels, frames, dst_frames);
|
||||
return dst_frames;
|
||||
}
|
||||
|
||||
static int rate_action(snd_pcm_plugin_t *plugin,
|
||||
snd_pcm_plugin_action_t action,
|
||||
unsigned long udata ATTRIBUTE_UNUSED)
|
||||
{
|
||||
snd_assert(plugin != NULL, return -ENXIO);
|
||||
switch (action) {
|
||||
case INIT:
|
||||
case PREPARE:
|
||||
rate_init(plugin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0; /* silenty ignore other actions */
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
int err;
|
||||
rate_t *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
|
||||
snd_assert(src_format->channels == dst_format->channels, return -ENXIO);
|
||||
snd_assert(src_format->channels > 0, return -ENXIO);
|
||||
snd_assert(snd_pcm_format_linear(src_format->format) != 0, return -ENXIO);
|
||||
snd_assert(snd_pcm_format_linear(dst_format->format) != 0, return -ENXIO);
|
||||
snd_assert(src_format->rate != dst_format->rate, return -ENXIO);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "rate conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(rate_t) + src_format->channels * sizeof(rate_channel_t),
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = (rate_t *)plugin->extra_data;
|
||||
data->get = getput_index(src_format->format);
|
||||
snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
|
||||
data->put = getput_index(dst_format->format);
|
||||
snd_assert(data->put >= 0 && data->put < 4*2*2, return -EINVAL);
|
||||
|
||||
if (src_format->rate < dst_format->rate) {
|
||||
data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
|
||||
data->func = resample_expand;
|
||||
} else {
|
||||
data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
|
||||
data->func = resample_shrink;
|
||||
}
|
||||
data->pos = 0;
|
||||
rate_init(plugin);
|
||||
data->old_src_frames = data->old_dst_frames = 0;
|
||||
plugin->transfer = rate_transfer;
|
||||
plugin->src_frames = rate_src_frames;
|
||||
plugin->dst_frames = rate_dst_frames;
|
||||
plugin->action = rate_action;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
519
sound/core/oss/route.c
Normal file
519
sound/core/oss/route.c
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Attenuated route Plug-In
|
||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "pcm_plugin.h"
|
||||
|
||||
/* The best possible hack to support missing optimization in gcc 2.7.2.3 */
|
||||
#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0
|
||||
#define div(a) a /= ROUTE_PLUGIN_RESOLUTION
|
||||
#elif ROUTE_PLUGIN_RESOLUTION == 16
|
||||
#define div(a) a >>= 4
|
||||
#else
|
||||
#error "Add some code here"
|
||||
#endif
|
||||
|
||||
typedef struct ttable_dst ttable_dst_t;
|
||||
typedef struct route_private_data route_t;
|
||||
|
||||
typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, snd_pcm_uframes_t frames);
|
||||
|
||||
typedef struct {
|
||||
int channel;
|
||||
int as_int;
|
||||
} ttable_src_t;
|
||||
|
||||
struct ttable_dst {
|
||||
int att; /* Attenuated */
|
||||
unsigned int nsrcs;
|
||||
ttable_src_t* srcs;
|
||||
route_channel_f func;
|
||||
};
|
||||
|
||||
struct route_private_data {
|
||||
enum {R_UINT32=0, R_UINT64=1} sum_type;
|
||||
int get, put;
|
||||
int conv;
|
||||
int src_sample_size;
|
||||
ttable_dst_t ttable[0];
|
||||
};
|
||||
|
||||
typedef union {
|
||||
u_int32_t as_uint32;
|
||||
u_int64_t as_uint64;
|
||||
} sum_t;
|
||||
|
||||
|
||||
static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable ATTRIBUTE_UNUSED, snd_pcm_uframes_t frames)
|
||||
{
|
||||
if (dst_channel->wanted)
|
||||
snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format);
|
||||
dst_channel->enabled = 0;
|
||||
}
|
||||
|
||||
static void route_to_channel_from_one(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, snd_pcm_uframes_t frames)
|
||||
{
|
||||
#define CONV_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_LABELS
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
void *conv;
|
||||
const snd_pcm_plugin_channel_t *src_channel = NULL;
|
||||
unsigned int srcidx;
|
||||
char *src, *dst;
|
||||
int src_step, dst_step;
|
||||
for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) {
|
||||
src_channel = &src_channels[ttable->srcs[srcidx].channel];
|
||||
if (src_channel->area.addr != NULL)
|
||||
break;
|
||||
}
|
||||
if (srcidx == ttable->nsrcs) {
|
||||
route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
}
|
||||
|
||||
dst_channel->enabled = 1;
|
||||
conv = conv_labels[data->conv];
|
||||
src = src_channel->area.addr + src_channel->area.first / 8;
|
||||
src_step = src_channel->area.step / 8;
|
||||
dst = dst_channel->area.addr + dst_channel->area.first / 8;
|
||||
dst_step = dst_channel->area.step / 8;
|
||||
while (frames-- > 0) {
|
||||
goto *conv;
|
||||
#define CONV_END after
|
||||
#include "plugin_ops.h"
|
||||
#undef CONV_END
|
||||
after:
|
||||
src += src_step;
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void route_to_channel(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channel,
|
||||
ttable_dst_t* ttable, snd_pcm_uframes_t frames)
|
||||
{
|
||||
#define GET_U_LABELS
|
||||
#define PUT_U32_LABELS
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_LABELS
|
||||
#undef PUT_U32_LABELS
|
||||
static void *zero_labels[2] = { &&zero_int32, &&zero_int64 };
|
||||
/* sum_type att */
|
||||
static void *add_labels[2 * 2] = { &&add_int32_noatt, &&add_int32_att,
|
||||
&&add_int64_noatt, &&add_int64_att,
|
||||
};
|
||||
/* sum_type att shift */
|
||||
static void *norm_labels[2 * 2 * 4] = { NULL,
|
||||
&&norm_int32_8_noatt,
|
||||
&&norm_int32_16_noatt,
|
||||
&&norm_int32_24_noatt,
|
||||
NULL,
|
||||
&&norm_int32_8_att,
|
||||
&&norm_int32_16_att,
|
||||
&&norm_int32_24_att,
|
||||
&&norm_int64_0_noatt,
|
||||
&&norm_int64_8_noatt,
|
||||
&&norm_int64_16_noatt,
|
||||
&&norm_int64_24_noatt,
|
||||
&&norm_int64_0_att,
|
||||
&&norm_int64_8_att,
|
||||
&&norm_int64_16_att,
|
||||
&&norm_int64_24_att,
|
||||
};
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
void *zero, *get, *add, *norm, *put_u32;
|
||||
int nsrcs = ttable->nsrcs;
|
||||
char *dst;
|
||||
int dst_step;
|
||||
char *srcs[nsrcs];
|
||||
int src_steps[nsrcs];
|
||||
ttable_src_t src_tt[nsrcs];
|
||||
u_int32_t sample = 0;
|
||||
int srcidx, srcidx1 = 0;
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel];
|
||||
if (!src_channel->enabled)
|
||||
continue;
|
||||
srcs[srcidx1] = src_channel->area.addr + src_channel->area.first / 8;
|
||||
src_steps[srcidx1] = src_channel->area.step / 8;
|
||||
src_tt[srcidx1] = ttable->srcs[srcidx];
|
||||
srcidx1++;
|
||||
}
|
||||
nsrcs = srcidx1;
|
||||
if (nsrcs == 0) {
|
||||
route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
} else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) {
|
||||
route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames);
|
||||
return;
|
||||
}
|
||||
|
||||
dst_channel->enabled = 1;
|
||||
zero = zero_labels[data->sum_type];
|
||||
get = get_u_labels[data->get];
|
||||
add = add_labels[data->sum_type * 2 + ttable->att];
|
||||
norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size];
|
||||
put_u32 = put_u32_labels[data->put];
|
||||
dst = dst_channel->area.addr + dst_channel->area.first / 8;
|
||||
dst_step = dst_channel->area.step / 8;
|
||||
|
||||
while (frames-- > 0) {
|
||||
ttable_src_t *ttp = src_tt;
|
||||
sum_t sum;
|
||||
|
||||
/* Zero sum */
|
||||
goto *zero;
|
||||
zero_int32:
|
||||
sum.as_uint32 = 0;
|
||||
goto zero_end;
|
||||
zero_int64:
|
||||
sum.as_uint64 = 0;
|
||||
goto zero_end;
|
||||
zero_end:
|
||||
for (srcidx = 0; srcidx < nsrcs; ++srcidx) {
|
||||
char *src = srcs[srcidx];
|
||||
|
||||
/* Get sample */
|
||||
goto *get;
|
||||
#define GET_U_END after_get
|
||||
#include "plugin_ops.h"
|
||||
#undef GET_U_END
|
||||
after_get:
|
||||
|
||||
/* Sum */
|
||||
goto *add;
|
||||
add_int32_att:
|
||||
sum.as_uint32 += sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int32_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint32 += sample;
|
||||
goto after_sum;
|
||||
add_int64_att:
|
||||
sum.as_uint64 += (u_int64_t) sample * ttp->as_int;
|
||||
goto after_sum;
|
||||
add_int64_noatt:
|
||||
if (ttp->as_int)
|
||||
sum.as_uint64 += sample;
|
||||
goto after_sum;
|
||||
after_sum:
|
||||
srcs[srcidx] += src_steps[srcidx];
|
||||
ttp++;
|
||||
}
|
||||
|
||||
/* Normalization */
|
||||
goto *norm;
|
||||
norm_int32_8_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_att:
|
||||
sum.as_uint64 <<= 8;
|
||||
norm_int64_0_att:
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_att:
|
||||
sum.as_uint64 <<= 16;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_att:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_att:
|
||||
sum.as_uint64 <<= 24;
|
||||
div(sum.as_uint64);
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_8_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_8_noatt:
|
||||
sum.as_uint64 <<= 8;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_16_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_16_noatt:
|
||||
sum.as_uint64 <<= 16;
|
||||
goto norm_int;
|
||||
|
||||
norm_int32_24_noatt:
|
||||
sum.as_uint64 = sum.as_uint32;
|
||||
norm_int64_24_noatt:
|
||||
sum.as_uint64 <<= 24;
|
||||
goto norm_int;
|
||||
|
||||
norm_int64_0_noatt:
|
||||
norm_int:
|
||||
if (sum.as_uint64 > (u_int32_t)0xffffffff)
|
||||
sample = (u_int32_t)0xffffffff;
|
||||
else
|
||||
sample = sum.as_uint64;
|
||||
goto after_norm;
|
||||
|
||||
after_norm:
|
||||
|
||||
/* Put sample */
|
||||
goto *put_u32;
|
||||
#define PUT_U32_END after_put_u32
|
||||
#include "plugin_ops.h"
|
||||
#undef PUT_U32_END
|
||||
after_put_u32:
|
||||
|
||||
dst += dst_step;
|
||||
}
|
||||
}
|
||||
|
||||
static int route_src_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *dst_vmask,
|
||||
bitset_t **src_vmask)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
int schannels = plugin->src_format.channels;
|
||||
int dchannels = plugin->dst_format.channels;
|
||||
bitset_t *vmask = plugin->src_vmask;
|
||||
int channel;
|
||||
ttable_dst_t *dp = data->ttable;
|
||||
bitset_zero(vmask, schannels);
|
||||
for (channel = 0; channel < dchannels; channel++, dp++) {
|
||||
unsigned int src;
|
||||
ttable_src_t *sp;
|
||||
if (!bitset_get(dst_vmask, channel))
|
||||
continue;
|
||||
sp = dp->srcs;
|
||||
for (src = 0; src < dp->nsrcs; src++, sp++)
|
||||
bitset_set(vmask, sp->channel);
|
||||
}
|
||||
*src_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int route_dst_channels_mask(snd_pcm_plugin_t *plugin,
|
||||
bitset_t *src_vmask,
|
||||
bitset_t **dst_vmask)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
int dchannels = plugin->dst_format.channels;
|
||||
bitset_t *vmask = plugin->dst_vmask;
|
||||
int channel;
|
||||
ttable_dst_t *dp = data->ttable;
|
||||
bitset_zero(vmask, dchannels);
|
||||
for (channel = 0; channel < dchannels; channel++, dp++) {
|
||||
unsigned int src;
|
||||
ttable_src_t *sp;
|
||||
sp = dp->srcs;
|
||||
for (src = 0; src < dp->nsrcs; src++, sp++) {
|
||||
if (bitset_get(src_vmask, sp->channel)) {
|
||||
bitset_set(vmask, channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst_vmask = vmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void route_free(snd_pcm_plugin_t *plugin)
|
||||
{
|
||||
route_t *data = (route_t *)plugin->extra_data;
|
||||
unsigned int dst_channel;
|
||||
for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
|
||||
kfree(data->ttable[dst_channel].srcs);
|
||||
}
|
||||
}
|
||||
|
||||
static int route_load_ttable(snd_pcm_plugin_t *plugin,
|
||||
const route_ttable_entry_t* src_ttable)
|
||||
{
|
||||
route_t *data;
|
||||
unsigned int src_channel, dst_channel;
|
||||
const route_ttable_entry_t *sptr;
|
||||
ttable_dst_t *dptr;
|
||||
if (src_ttable == NULL)
|
||||
return 0;
|
||||
data = (route_t *)plugin->extra_data;
|
||||
dptr = data->ttable;
|
||||
sptr = src_ttable;
|
||||
plugin->private_free = route_free;
|
||||
for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) {
|
||||
route_ttable_entry_t t = 0;
|
||||
int att = 0;
|
||||
int nsrcs = 0;
|
||||
ttable_src_t srcs[plugin->src_format.channels];
|
||||
for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) {
|
||||
snd_assert(*sptr >= 0 || *sptr <= FULL, return -ENXIO);
|
||||
if (*sptr != 0) {
|
||||
srcs[nsrcs].channel = src_channel;
|
||||
srcs[nsrcs].as_int = *sptr;
|
||||
if (*sptr != FULL)
|
||||
att = 1;
|
||||
t += *sptr;
|
||||
nsrcs++;
|
||||
}
|
||||
sptr++;
|
||||
}
|
||||
dptr->att = att;
|
||||
dptr->nsrcs = nsrcs;
|
||||
if (nsrcs == 0)
|
||||
dptr->func = route_to_channel_from_zero;
|
||||
else if (nsrcs == 1 && !att)
|
||||
dptr->func = route_to_channel_from_one;
|
||||
else
|
||||
dptr->func = route_to_channel;
|
||||
if (nsrcs > 0) {
|
||||
int srcidx;
|
||||
dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL);
|
||||
for(srcidx = 0; srcidx < nsrcs; srcidx++)
|
||||
dptr->srcs[srcidx] = srcs[srcidx];
|
||||
} else
|
||||
dptr->srcs = NULL;
|
||||
dptr++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t route_transfer(snd_pcm_plugin_t *plugin,
|
||||
const snd_pcm_plugin_channel_t *src_channels,
|
||||
snd_pcm_plugin_channel_t *dst_channels,
|
||||
snd_pcm_uframes_t frames)
|
||||
{
|
||||
route_t *data;
|
||||
int src_nchannels, dst_nchannels;
|
||||
int dst_channel;
|
||||
ttable_dst_t *ttp;
|
||||
snd_pcm_plugin_channel_t *dvp;
|
||||
|
||||
snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO);
|
||||
if (frames == 0)
|
||||
return 0;
|
||||
data = (route_t *)plugin->extra_data;
|
||||
|
||||
src_nchannels = plugin->src_format.channels;
|
||||
dst_nchannels = plugin->dst_format.channels;
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
{
|
||||
int src_channel;
|
||||
for (src_channel = 0; src_channel < src_nchannels; ++src_channel) {
|
||||
snd_assert(src_channels[src_channel].area.first % 8 == 0 ||
|
||||
src_channels[src_channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
}
|
||||
for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
|
||||
snd_assert(dst_channels[dst_channel].area.first % 8 == 0 ||
|
||||
dst_channels[dst_channel].area.step % 8 == 0,
|
||||
return -ENXIO);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ttp = data->ttable;
|
||||
dvp = dst_channels;
|
||||
for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) {
|
||||
ttp->func(plugin, src_channels, dvp, ttp, frames);
|
||||
dvp++;
|
||||
ttp++;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
int getput_index(int format)
|
||||
{
|
||||
int sign, width, endian;
|
||||
sign = !snd_pcm_format_signed(format);
|
||||
width = snd_pcm_format_width(format) / 8 - 1;
|
||||
if (width < 0 || width > 3) {
|
||||
snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
|
||||
width = 0;
|
||||
}
|
||||
#ifdef SNDRV_LITTLE_ENDIAN
|
||||
endian = snd_pcm_format_big_endian(format);
|
||||
#else
|
||||
endian = snd_pcm_format_little_endian(format);
|
||||
#endif
|
||||
if (endian < 0)
|
||||
endian = 0;
|
||||
return width * 4 + endian * 2 + sign;
|
||||
}
|
||||
|
||||
int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug,
|
||||
snd_pcm_plugin_format_t *src_format,
|
||||
snd_pcm_plugin_format_t *dst_format,
|
||||
route_ttable_entry_t *ttable,
|
||||
snd_pcm_plugin_t **r_plugin)
|
||||
{
|
||||
route_t *data;
|
||||
snd_pcm_plugin_t *plugin;
|
||||
int err;
|
||||
|
||||
snd_assert(r_plugin != NULL, return -ENXIO);
|
||||
*r_plugin = NULL;
|
||||
snd_assert(src_format->rate == dst_format->rate, return -ENXIO);
|
||||
snd_assert(snd_pcm_format_linear(src_format->format) != 0 &&
|
||||
snd_pcm_format_linear(dst_format->format) != 0,
|
||||
return -ENXIO);
|
||||
|
||||
err = snd_pcm_plugin_build(plug, "attenuated route conversion",
|
||||
src_format, dst_format,
|
||||
sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels,
|
||||
&plugin);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = (route_t *) plugin->extra_data;
|
||||
|
||||
data->get = getput_index(src_format->format);
|
||||
snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
|
||||
data->put = getput_index(dst_format->format);
|
||||
snd_assert(data->get >= 0 && data->get < 4*2*2, return -EINVAL);
|
||||
data->conv = conv_index(src_format->format, dst_format->format);
|
||||
|
||||
if (snd_pcm_format_width(src_format->format) == 32)
|
||||
data->sum_type = R_UINT64;
|
||||
else
|
||||
data->sum_type = R_UINT32;
|
||||
data->src_sample_size = snd_pcm_format_width(src_format->format) / 8;
|
||||
|
||||
if ((err = route_load_ttable(plugin, ttable)) < 0) {
|
||||
snd_pcm_plugin_free(plugin);
|
||||
return err;
|
||||
}
|
||||
plugin->transfer = route_transfer;
|
||||
plugin->src_channels_mask = route_src_channels_mask;
|
||||
plugin->dst_channels_mask = route_dst_channels_mask;
|
||||
*r_plugin = plugin;
|
||||
return 0;
|
||||
}
|
||||
1074
sound/core/pcm.c
Normal file
1074
sound/core/pcm.c
Normal file
File diff suppressed because it is too large
Load Diff
513
sound/core/pcm_compat.c
Normal file
513
sound/core/pcm_compat.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* 32bit -> 64bit ioctl wrapper for PCM API
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file included from pcm_native.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
static int snd_pcm_ioctl_delay_compat(snd_pcm_substream_t *substream,
|
||||
s32 __user *src)
|
||||
{
|
||||
snd_pcm_sframes_t delay;
|
||||
mm_segment_t fs;
|
||||
int err;
|
||||
|
||||
fs = snd_enter_user();
|
||||
err = snd_pcm_delay(substream, &delay);
|
||||
snd_leave_user(fs);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (put_user(delay, src))
|
||||
return -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_pcm_ioctl_rewind_compat(snd_pcm_substream_t *substream,
|
||||
u32 __user *src)
|
||||
{
|
||||
snd_pcm_uframes_t frames;
|
||||
int err;
|
||||
|
||||
if (get_user(frames, src))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_playback_rewind(substream, frames);
|
||||
else
|
||||
err = snd_pcm_capture_rewind(substream, frames);
|
||||
if (put_user(err, src))
|
||||
return -EFAULT;
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_ioctl_forward_compat(snd_pcm_substream_t *substream,
|
||||
u32 __user *src)
|
||||
{
|
||||
snd_pcm_uframes_t frames;
|
||||
int err;
|
||||
|
||||
if (get_user(frames, src))
|
||||
return -EFAULT;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_playback_forward(substream, frames);
|
||||
else
|
||||
err = snd_pcm_capture_forward(substream, frames);
|
||||
if (put_user(err, src))
|
||||
return -EFAULT;
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
struct sndrv_pcm_hw_params32 {
|
||||
u32 flags;
|
||||
struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
|
||||
struct sndrv_mask mres[5]; /* reserved masks */
|
||||
struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
|
||||
struct sndrv_interval ires[9]; /* reserved intervals */
|
||||
u32 rmask;
|
||||
u32 cmask;
|
||||
u32 info;
|
||||
u32 msbits;
|
||||
u32 rate_num;
|
||||
u32 rate_den;
|
||||
u32 fifo_size;
|
||||
unsigned char reserved[64];
|
||||
};
|
||||
|
||||
struct sndrv_pcm_sw_params32 {
|
||||
s32 tstamp_mode;
|
||||
u32 period_step;
|
||||
u32 sleep_min;
|
||||
u32 avail_min;
|
||||
u32 xfer_align;
|
||||
u32 start_threshold;
|
||||
u32 stop_threshold;
|
||||
u32 silence_threshold;
|
||||
u32 silence_size;
|
||||
u32 boundary;
|
||||
unsigned char reserved[64];
|
||||
};
|
||||
|
||||
static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream,
|
||||
struct sndrv_pcm_sw_params32 __user *src)
|
||||
{
|
||||
snd_pcm_sw_params_t params;
|
||||
int err;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
|
||||
get_user(params.period_step, &src->period_step) ||
|
||||
get_user(params.sleep_min, &src->sleep_min) ||
|
||||
get_user(params.avail_min, &src->avail_min) ||
|
||||
get_user(params.xfer_align, &src->xfer_align) ||
|
||||
get_user(params.start_threshold, &src->start_threshold) ||
|
||||
get_user(params.stop_threshold, &src->stop_threshold) ||
|
||||
get_user(params.silence_threshold, &src->silence_threshold) ||
|
||||
get_user(params.silence_size, &src->silence_size))
|
||||
return -EFAULT;
|
||||
err = snd_pcm_sw_params(substream, ¶ms);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (put_user(params.boundary, &src->boundary))
|
||||
return -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
struct sndrv_pcm_channel_info32 {
|
||||
u32 channel;
|
||||
u32 offset;
|
||||
u32 first;
|
||||
u32 step;
|
||||
};
|
||||
|
||||
static int snd_pcm_ioctl_channel_info_compat(snd_pcm_substream_t *substream,
|
||||
struct sndrv_pcm_channel_info32 __user *src)
|
||||
{
|
||||
snd_pcm_channel_info_t info;
|
||||
int err;
|
||||
|
||||
if (get_user(info.channel, &src->channel) ||
|
||||
get_user(info.offset, &src->offset) ||
|
||||
get_user(info.first, &src->first) ||
|
||||
get_user(info.step, &src->step))
|
||||
return -EFAULT;
|
||||
err = snd_pcm_channel_info(substream, &info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (put_user(info.channel, &src->channel) ||
|
||||
put_user(info.offset, &src->offset) ||
|
||||
put_user(info.first, &src->first) ||
|
||||
put_user(info.step, &src->step))
|
||||
return -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
struct sndrv_pcm_status32 {
|
||||
s32 state;
|
||||
struct compat_timespec trigger_tstamp;
|
||||
struct compat_timespec tstamp;
|
||||
u32 appl_ptr;
|
||||
u32 hw_ptr;
|
||||
s32 delay;
|
||||
u32 avail;
|
||||
u32 avail_max;
|
||||
u32 overrange;
|
||||
s32 suspended_state;
|
||||
unsigned char reserved[60];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
static int snd_pcm_status_user_compat(snd_pcm_substream_t *substream,
|
||||
struct sndrv_pcm_status32 __user *src)
|
||||
{
|
||||
snd_pcm_status_t status;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_status(substream, &status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (put_user(status.state, &src->state) ||
|
||||
put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
|
||||
put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
|
||||
put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
|
||||
put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
|
||||
put_user(status.appl_ptr, &src->appl_ptr) ||
|
||||
put_user(status.hw_ptr, &src->hw_ptr) ||
|
||||
put_user(status.delay, &src->delay) ||
|
||||
put_user(status.avail, &src->avail) ||
|
||||
put_user(status.avail_max, &src->avail_max) ||
|
||||
put_user(status.overrange, &src->overrange) ||
|
||||
put_user(status.suspended_state, &src->suspended_state))
|
||||
return -EFAULT;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* recalcuate the boundary within 32bit */
|
||||
static void recalculate_boundary(snd_pcm_runtime_t *runtime)
|
||||
{
|
||||
if (! runtime->buffer_size)
|
||||
return;
|
||||
runtime->boundary = runtime->buffer_size;
|
||||
while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
|
||||
runtime->boundary *= 2;
|
||||
}
|
||||
|
||||
/* both for HW_PARAMS and HW_REFINE */
|
||||
static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream,
|
||||
int refine,
|
||||
struct sndrv_pcm_hw_params32 __user *data32)
|
||||
{
|
||||
struct sndrv_pcm_hw_params *data;
|
||||
snd_pcm_runtime_t *runtime;
|
||||
int err;
|
||||
|
||||
if (! (runtime = substream->runtime))
|
||||
return -ENOTTY;
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
/* only fifo_size is different, so just copy all */
|
||||
if (copy_from_user(data, data32, sizeof(*data32))) {
|
||||
err = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
if (refine)
|
||||
err = snd_pcm_hw_refine(substream, data);
|
||||
else
|
||||
err = snd_pcm_hw_params(substream, data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (copy_to_user(data32, data, sizeof(*data32)) ||
|
||||
put_user(data->fifo_size, &data32->fifo_size)) {
|
||||
err = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (! refine)
|
||||
recalculate_boundary(runtime);
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
struct sndrv_xferi32 {
|
||||
s32 result;
|
||||
u32 buf;
|
||||
u32 frames;
|
||||
};
|
||||
|
||||
static int snd_pcm_ioctl_xferi_compat(snd_pcm_substream_t *substream,
|
||||
int dir, struct sndrv_xferi32 __user *data32)
|
||||
{
|
||||
compat_caddr_t buf;
|
||||
u32 frames;
|
||||
int err;
|
||||
|
||||
if (! substream->runtime)
|
||||
return -ENOTTY;
|
||||
if (substream->stream != dir)
|
||||
return -EINVAL;
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
return -EBADFD;
|
||||
|
||||
if (get_user(buf, &data32->buf) ||
|
||||
get_user(frames, &data32->frames))
|
||||
return -EFAULT;
|
||||
|
||||
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
|
||||
else
|
||||
err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* copy the result */
|
||||
if (put_user(err, &data32->result))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* snd_xfern needs remapping of bufs */
|
||||
struct sndrv_xfern32 {
|
||||
s32 result;
|
||||
u32 bufs; /* this is void **; */
|
||||
u32 frames;
|
||||
};
|
||||
|
||||
/*
|
||||
* xfern ioctl nees to copy (up to) 128 pointers on stack.
|
||||
* although we may pass the copied pointers through f_op->ioctl, but the ioctl
|
||||
* handler there expands again the same 128 pointers on stack, so it is better
|
||||
* to handle the function (calling pcm_readv/writev) directly in this handler.
|
||||
*/
|
||||
static int snd_pcm_ioctl_xfern_compat(snd_pcm_substream_t *substream,
|
||||
int dir, struct sndrv_xfern32 __user *data32)
|
||||
{
|
||||
compat_caddr_t buf;
|
||||
compat_caddr_t __user *bufptr;
|
||||
u32 frames;
|
||||
void __user **bufs;
|
||||
int err, ch, i;
|
||||
|
||||
if (! substream->runtime)
|
||||
return -ENOTTY;
|
||||
if (substream->stream != dir)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ch = substream->runtime->channels) > 128)
|
||||
return -EINVAL;
|
||||
if (get_user(buf, &data32->bufs) ||
|
||||
get_user(frames, &data32->frames))
|
||||
return -EFAULT;
|
||||
bufptr = compat_ptr(buf);
|
||||
bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
|
||||
if (bufs == NULL)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < ch; i++) {
|
||||
u32 ptr;
|
||||
if (get_user(ptr, bufptr)) {
|
||||
kfree(bufs);
|
||||
return -EFAULT;
|
||||
}
|
||||
bufs[ch] = compat_ptr(ptr);
|
||||
bufptr++;
|
||||
}
|
||||
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
err = snd_pcm_lib_writev(substream, bufs, frames);
|
||||
else
|
||||
err = snd_pcm_lib_readv(substream, bufs, frames);
|
||||
if (err >= 0) {
|
||||
if (put_user(err, &data32->result))
|
||||
err = -EFAULT;
|
||||
}
|
||||
kfree(bufs);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
struct sndrv_pcm_mmap_status32 {
|
||||
s32 state;
|
||||
s32 pad1;
|
||||
u32 hw_ptr;
|
||||
struct compat_timespec tstamp;
|
||||
s32 suspended_state;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sndrv_pcm_mmap_control32 {
|
||||
u32 appl_ptr;
|
||||
u32 avail_min;
|
||||
};
|
||||
|
||||
struct sndrv_pcm_sync_ptr32 {
|
||||
u32 flags;
|
||||
union {
|
||||
struct sndrv_pcm_mmap_status32 status;
|
||||
unsigned char reserved[64];
|
||||
} s;
|
||||
union {
|
||||
struct sndrv_pcm_mmap_control32 control;
|
||||
unsigned char reserved[64];
|
||||
} c;
|
||||
} __attribute__((packed));
|
||||
|
||||
static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream,
|
||||
struct sndrv_pcm_sync_ptr32 __user *src)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
volatile struct sndrv_pcm_mmap_status *status;
|
||||
volatile struct sndrv_pcm_mmap_control *control;
|
||||
u32 sflags;
|
||||
struct sndrv_pcm_mmap_control scontrol;
|
||||
struct sndrv_pcm_mmap_status sstatus;
|
||||
int err;
|
||||
|
||||
snd_assert(runtime, return -EINVAL);
|
||||
|
||||
if (get_user(sflags, &src->flags) ||
|
||||
get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
|
||||
get_user(scontrol.avail_min, &src->c.control.avail_min))
|
||||
return -EFAULT;
|
||||
if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
|
||||
err = snd_pcm_hwsync(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
status = runtime->status;
|
||||
control = runtime->control;
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
|
||||
control->appl_ptr = scontrol.appl_ptr;
|
||||
else
|
||||
scontrol.appl_ptr = control->appl_ptr;
|
||||
if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
|
||||
control->avail_min = scontrol.avail_min;
|
||||
else
|
||||
scontrol.avail_min = control->avail_min;
|
||||
sstatus.state = status->state;
|
||||
sstatus.hw_ptr = status->hw_ptr;
|
||||
sstatus.tstamp = status->tstamp;
|
||||
sstatus.suspended_state = status->suspended_state;
|
||||
snd_pcm_stream_unlock_irq(substream);
|
||||
if (put_user(sstatus.state, &src->s.status.state) ||
|
||||
put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
|
||||
put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
|
||||
put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
|
||||
put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
|
||||
put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
|
||||
put_user(scontrol.avail_min, &src->c.control.avail_min))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
enum {
|
||||
SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32),
|
||||
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32),
|
||||
SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32),
|
||||
SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32),
|
||||
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
|
||||
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32),
|
||||
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
|
||||
SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
|
||||
SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32),
|
||||
SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32),
|
||||
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32),
|
||||
SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32),
|
||||
SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32),
|
||||
|
||||
};
|
||||
|
||||
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_pcm_file_t *pcm_file;
|
||||
snd_pcm_substream_t *substream;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
|
||||
pcm_file = file->private_data;
|
||||
if (! pcm_file)
|
||||
return -ENOTTY;
|
||||
substream = pcm_file->substream;
|
||||
if (! substream)
|
||||
return -ENOTTY;
|
||||
|
||||
/*
|
||||
* When PCM is used on 32bit mode, we need to disable
|
||||
* mmap of PCM status/control records because of the size
|
||||
* incompatibility.
|
||||
*/
|
||||
substream->no_mmap_ctrl = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_IOCTL_PVERSION:
|
||||
case SNDRV_PCM_IOCTL_INFO:
|
||||
case SNDRV_PCM_IOCTL_TSTAMP:
|
||||
case SNDRV_PCM_IOCTL_HWSYNC:
|
||||
case SNDRV_PCM_IOCTL_PREPARE:
|
||||
case SNDRV_PCM_IOCTL_RESET:
|
||||
case SNDRV_PCM_IOCTL_START:
|
||||
case SNDRV_PCM_IOCTL_DROP:
|
||||
case SNDRV_PCM_IOCTL_DRAIN:
|
||||
case SNDRV_PCM_IOCTL_PAUSE:
|
||||
case SNDRV_PCM_IOCTL_HW_FREE:
|
||||
case SNDRV_PCM_IOCTL_RESUME:
|
||||
case SNDRV_PCM_IOCTL_XRUN:
|
||||
case SNDRV_PCM_IOCTL_LINK:
|
||||
case SNDRV_PCM_IOCTL_UNLINK:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return snd_pcm_playback_ioctl1(substream, cmd, argp);
|
||||
else
|
||||
return snd_pcm_capture_ioctl1(substream, cmd, argp);
|
||||
case SNDRV_PCM_IOCTL_HW_REFINE32:
|
||||
return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
|
||||
case SNDRV_PCM_IOCTL_HW_PARAMS32:
|
||||
return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
|
||||
case SNDRV_PCM_IOCTL_SW_PARAMS32:
|
||||
return snd_pcm_ioctl_sw_params_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_STATUS32:
|
||||
return snd_pcm_status_user_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_SYNC_PTR32:
|
||||
return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
|
||||
return snd_pcm_ioctl_channel_info_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
|
||||
return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
|
||||
case SNDRV_PCM_IOCTL_READI_FRAMES32:
|
||||
return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
|
||||
case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
|
||||
return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
|
||||
case SNDRV_PCM_IOCTL_READN_FRAMES32:
|
||||
return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
|
||||
case SNDRV_PCM_IOCTL_DELAY32:
|
||||
return snd_pcm_ioctl_delay_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_REWIND32:
|
||||
return snd_pcm_ioctl_rewind_compat(substream, argp);
|
||||
case SNDRV_PCM_IOCTL_FORWARD32:
|
||||
return snd_pcm_ioctl_forward_compat(substream, argp);
|
||||
}
|
||||
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
2612
sound/core/pcm_lib.c
Normal file
2612
sound/core/pcm_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
363
sound/core/pcm_memory.c
Normal file
363
sound/core/pcm_memory.c
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Digital Audio (PCM) abstract layer
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
static int preallocate_dma = 1;
|
||||
module_param(preallocate_dma, int, 0444);
|
||||
MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized.");
|
||||
|
||||
static int maximum_substreams = 4;
|
||||
module_param(maximum_substreams, int, 0444);
|
||||
MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory.");
|
||||
|
||||
static const size_t snd_minimum_buffer = 16384;
|
||||
|
||||
|
||||
/*
|
||||
* try to allocate as the large pages as possible.
|
||||
* stores the resultant memory size in *res_size.
|
||||
*
|
||||
* the minimum size is snd_minimum_buffer. it should be power of 2.
|
||||
*/
|
||||
static int preallocate_pcm_pages(snd_pcm_substream_t *substream, size_t size)
|
||||
{
|
||||
struct snd_dma_buffer *dmab = &substream->dma_buffer;
|
||||
int err;
|
||||
|
||||
snd_assert(size > 0, return -EINVAL);
|
||||
|
||||
/* already reserved? */
|
||||
if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
|
||||
if (dmab->bytes >= size)
|
||||
return 0; /* yes */
|
||||
/* no, free the reserved block */
|
||||
snd_dma_free_pages(dmab);
|
||||
dmab->bytes = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
|
||||
size, dmab)) < 0) {
|
||||
if (err != -ENOMEM)
|
||||
return err; /* fatal error */
|
||||
} else
|
||||
return 0;
|
||||
size >>= 1;
|
||||
} while (size >= snd_minimum_buffer);
|
||||
dmab->bytes = 0; /* tell error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release the preallocated buffer if not yet done.
|
||||
*/
|
||||
static void snd_pcm_lib_preallocate_dma_free(snd_pcm_substream_t *substream)
|
||||
{
|
||||
if (substream->dma_buffer.area == NULL)
|
||||
return;
|
||||
if (substream->dma_buf_id)
|
||||
snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id);
|
||||
else
|
||||
snd_dma_free_pages(&substream->dma_buffer);
|
||||
substream->dma_buffer.area = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream.
|
||||
* @substream: the pcm substream instance
|
||||
*
|
||||
* Releases the pre-allocated buffer of the given substream.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_pcm_lib_preallocate_dma_free(substream);
|
||||
if (substream->proc_prealloc_entry) {
|
||||
snd_info_unregister(substream->proc_prealloc_entry);
|
||||
substream->proc_prealloc_entry = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm
|
||||
* @pcm: the pcm instance
|
||||
*
|
||||
* Releases all the pre-allocated buffers on the given pcm.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm)
|
||||
{
|
||||
snd_pcm_substream_t *substream;
|
||||
int stream;
|
||||
|
||||
for (stream = 0; stream < 2; stream++)
|
||||
for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
|
||||
snd_pcm_lib_preallocate_free(substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* read callback for prealloc proc file
|
||||
*
|
||||
* prints the current allocated size in kB.
|
||||
*/
|
||||
static void snd_pcm_lib_preallocate_proc_read(snd_info_entry_t *entry,
|
||||
snd_info_buffer_t *buffer)
|
||||
{
|
||||
snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
|
||||
snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024);
|
||||
}
|
||||
|
||||
/*
|
||||
* write callback for prealloc proc file
|
||||
*
|
||||
* accepts the preallocation size in kB.
|
||||
*/
|
||||
static void snd_pcm_lib_preallocate_proc_write(snd_info_entry_t *entry,
|
||||
snd_info_buffer_t *buffer)
|
||||
{
|
||||
snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data;
|
||||
char line[64], str[64];
|
||||
size_t size;
|
||||
struct snd_dma_buffer new_dmab;
|
||||
|
||||
if (substream->runtime) {
|
||||
buffer->error = -EBUSY;
|
||||
return;
|
||||
}
|
||||
if (!snd_info_get_line(buffer, line, sizeof(line))) {
|
||||
snd_info_get_str(str, line, sizeof(str));
|
||||
size = simple_strtoul(str, NULL, 10) * 1024;
|
||||
if ((size != 0 && size < 8192) || size > substream->dma_max) {
|
||||
buffer->error = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (substream->dma_buffer.bytes == size)
|
||||
return;
|
||||
memset(&new_dmab, 0, sizeof(new_dmab));
|
||||
new_dmab.dev = substream->dma_buffer.dev;
|
||||
if (size > 0) {
|
||||
if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
|
||||
substream->dma_buffer.dev.dev,
|
||||
size, &new_dmab) < 0) {
|
||||
buffer->error = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
substream->buffer_bytes_max = size;
|
||||
} else {
|
||||
substream->buffer_bytes_max = UINT_MAX;
|
||||
}
|
||||
if (substream->dma_buffer.area)
|
||||
snd_dma_free_pages(&substream->dma_buffer);
|
||||
substream->dma_buffer = new_dmab;
|
||||
} else {
|
||||
buffer->error = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pre-allocate the buffer and create a proc file for the substream
|
||||
*/
|
||||
static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream,
|
||||
size_t size, size_t max)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
|
||||
preallocate_pcm_pages(substream, size);
|
||||
|
||||
if (substream->dma_buffer.bytes > 0)
|
||||
substream->buffer_bytes_max = substream->dma_buffer.bytes;
|
||||
substream->dma_max = max;
|
||||
if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
|
||||
entry->c.text.read_size = 64;
|
||||
entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
|
||||
entry->c.text.write_size = 64;
|
||||
entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
|
||||
entry->private_data = substream;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
substream->proc_prealloc_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
|
||||
* @substream: the pcm substream instance
|
||||
* @type: DMA type (SNDRV_DMA_TYPE_*)
|
||||
* @data: DMA type dependant data
|
||||
* @size: the requested pre-allocation size in bytes
|
||||
* @max: the max. allowed pre-allocation size
|
||||
*
|
||||
* Do pre-allocation for the given DMA buffer type.
|
||||
*
|
||||
* When substream->dma_buf_id is set, the function tries to look for
|
||||
* the reserved buffer, and the buffer is not freed but reserved at
|
||||
* destruction time. The dma_buf_id must be unique for all systems
|
||||
* (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream,
|
||||
int type, struct device *data,
|
||||
size_t size, size_t max)
|
||||
{
|
||||
substream->dma_buffer.dev.type = type;
|
||||
substream->dma_buffer.dev.dev = data;
|
||||
return snd_pcm_lib_preallocate_pages1(substream, size, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
|
||||
* @substream: the pcm substream instance
|
||||
* @type: DMA type (SNDRV_DMA_TYPE_*)
|
||||
* @data: DMA type dependant data
|
||||
* @size: the requested pre-allocation size in bytes
|
||||
* @max: the max. allowed pre-allocation size
|
||||
*
|
||||
* Do pre-allocation to all substreams of the given pcm for the
|
||||
* specified DMA type.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm,
|
||||
int type, void *data,
|
||||
size_t size, size_t max)
|
||||
{
|
||||
snd_pcm_substream_t *substream;
|
||||
int stream, err;
|
||||
|
||||
for (stream = 0; stream < 2; stream++)
|
||||
for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
|
||||
if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
|
||||
* @substream: the pcm substream instance
|
||||
* @offset: the buffer offset
|
||||
*
|
||||
* Returns the page struct at the given buffer offset.
|
||||
* Used as the page callback of PCM ops.
|
||||
*/
|
||||
struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
|
||||
|
||||
unsigned int idx = offset >> PAGE_SHIFT;
|
||||
if (idx >= (unsigned int)sgbuf->pages)
|
||||
return NULL;
|
||||
return sgbuf->page_table[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
|
||||
* @substream: the substream to allocate the DMA buffer to
|
||||
* @size: the requested buffer size in bytes
|
||||
*
|
||||
* Allocates the DMA buffer on the BUS type given earlier to
|
||||
* snd_pcm_lib_preallocate_xxx_pages().
|
||||
*
|
||||
* Returns 1 if the buffer is changed, 0 if not changed, or a negative
|
||||
* code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime;
|
||||
struct snd_dma_buffer *dmab = NULL;
|
||||
|
||||
snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL);
|
||||
snd_assert(substream != NULL, return -EINVAL);
|
||||
runtime = substream->runtime;
|
||||
snd_assert(runtime != NULL, return -EINVAL);
|
||||
|
||||
if (runtime->dma_buffer_p) {
|
||||
/* perphaps, we might free the large DMA memory region
|
||||
to save some space here, but the actual solution
|
||||
costs us less time */
|
||||
if (runtime->dma_buffer_p->bytes >= size) {
|
||||
runtime->dma_bytes = size;
|
||||
return 0; /* ok, do not change */
|
||||
}
|
||||
snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
|
||||
dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
|
||||
} else {
|
||||
dmab = kcalloc(1, sizeof(*dmab), GFP_KERNEL);
|
||||
if (! dmab)
|
||||
return -ENOMEM;
|
||||
dmab->dev = substream->dma_buffer.dev;
|
||||
if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
|
||||
substream->dma_buffer.dev.dev,
|
||||
size, dmab) < 0) {
|
||||
kfree(dmab);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
snd_pcm_set_runtime_buffer(substream, dmab);
|
||||
runtime->dma_bytes = size;
|
||||
return 1; /* area was changed */
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_free_pages - release the allocated DMA buffer.
|
||||
* @substream: the substream to release the DMA buffer
|
||||
*
|
||||
* Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_pcm_runtime_t *runtime;
|
||||
|
||||
snd_assert(substream != NULL, return -EINVAL);
|
||||
runtime = substream->runtime;
|
||||
snd_assert(runtime != NULL, return -EINVAL);
|
||||
if (runtime->dma_area == NULL)
|
||||
return 0;
|
||||
if (runtime->dma_buffer_p != &substream->dma_buffer) {
|
||||
/* it's a newly allocated buffer. release it now. */
|
||||
snd_dma_free_pages(runtime->dma_buffer_p);
|
||||
kfree(runtime->dma_buffer_p);
|
||||
}
|
||||
snd_pcm_set_runtime_buffer(substream, NULL);
|
||||
return 0;
|
||||
}
|
||||
481
sound/core/pcm_misc.c
Normal file
481
sound/core/pcm_misc.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* PCM Interface - misc routines
|
||||
* Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#define SND_PCM_FORMAT_UNKNOWN (-1)
|
||||
|
||||
/* NOTE: "signed" prefix must be given below since the default char is
|
||||
* unsigned on some architectures!
|
||||
*/
|
||||
struct pcm_format_data {
|
||||
unsigned char width; /* bit width */
|
||||
unsigned char phys; /* physical bit width */
|
||||
signed char le; /* 0 = big-endian, 1 = little-endian, -1 = others */
|
||||
signed char signd; /* 0 = unsigned, 1 = signed, -1 = others */
|
||||
unsigned char silence[8]; /* silence data to fill */
|
||||
};
|
||||
|
||||
static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
|
||||
[SNDRV_PCM_FORMAT_S8] = {
|
||||
.width = 8, .phys = 8, .le = -1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U8] = {
|
||||
.width = 8, .phys = 8, .le = -1, .signd = 0,
|
||||
.silence = { 0x80 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S16_LE] = {
|
||||
.width = 16, .phys = 16, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S16_BE] = {
|
||||
.width = 16, .phys = 16, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U16_LE] = {
|
||||
.width = 16, .phys = 16, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x80 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U16_BE] = {
|
||||
.width = 16, .phys = 16, .le = 0, .signd = 0,
|
||||
.silence = { 0x80, 0x00 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S24_LE] = {
|
||||
.width = 24, .phys = 32, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S24_BE] = {
|
||||
.width = 24, .phys = 32, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U24_LE] = {
|
||||
.width = 24, .phys = 32, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x00, 0x80 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U24_BE] = {
|
||||
.width = 24, .phys = 32, .le = 0, .signd = 0,
|
||||
.silence = { 0x80, 0x00, 0x00 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S32_LE] = {
|
||||
.width = 32, .phys = 32, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S32_BE] = {
|
||||
.width = 32, .phys = 32, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U32_LE] = {
|
||||
.width = 32, .phys = 32, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x00, 0x00, 0x80 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U32_BE] = {
|
||||
.width = 32, .phys = 32, .le = 0, .signd = 0,
|
||||
.silence = { 0x80, 0x00, 0x00, 0x00 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_FLOAT_LE] = {
|
||||
.width = 32, .phys = 32, .le = 1, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_FLOAT_BE] = {
|
||||
.width = 32, .phys = 32, .le = 0, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_FLOAT64_LE] = {
|
||||
.width = 64, .phys = 64, .le = 1, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_FLOAT64_BE] = {
|
||||
.width = 64, .phys = 64, .le = 0, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE] = {
|
||||
.width = 32, .phys = 32, .le = 1, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE] = {
|
||||
.width = 32, .phys = 32, .le = 0, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_MU_LAW] = {
|
||||
.width = 8, .phys = 8, .le = -1, .signd = -1,
|
||||
.silence = { 0x7f },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_A_LAW] = {
|
||||
.width = 8, .phys = 8, .le = -1, .signd = -1,
|
||||
.silence = { 0x55 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_IMA_ADPCM] = {
|
||||
.width = 4, .phys = 4, .le = -1, .signd = -1,
|
||||
.silence = {},
|
||||
},
|
||||
/* FIXME: the following three formats are not defined properly yet */
|
||||
[SNDRV_PCM_FORMAT_MPEG] = {
|
||||
.le = -1, .signd = -1,
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_GSM] = {
|
||||
.le = -1, .signd = -1,
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_SPECIAL] = {
|
||||
.le = -1, .signd = -1,
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S24_3LE] = {
|
||||
.width = 24, .phys = 24, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S24_3BE] = {
|
||||
.width = 24, .phys = 24, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U24_3LE] = {
|
||||
.width = 24, .phys = 24, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x00, 0x80 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U24_3BE] = {
|
||||
.width = 24, .phys = 24, .le = 0, .signd = 0,
|
||||
.silence = { 0x80, 0x00, 0x00 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S20_3LE] = {
|
||||
.width = 20, .phys = 24, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S20_3BE] = {
|
||||
.width = 20, .phys = 24, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U20_3LE] = {
|
||||
.width = 20, .phys = 24, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x00, 0x08 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U20_3BE] = {
|
||||
.width = 20, .phys = 24, .le = 0, .signd = 0,
|
||||
.silence = { 0x08, 0x00, 0x00 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S18_3LE] = {
|
||||
.width = 18, .phys = 24, .le = 1, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_S18_3BE] = {
|
||||
.width = 18, .phys = 24, .le = 0, .signd = 1,
|
||||
.silence = {},
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U18_3LE] = {
|
||||
.width = 18, .phys = 24, .le = 1, .signd = 0,
|
||||
.silence = { 0x00, 0x00, 0x02 },
|
||||
},
|
||||
[SNDRV_PCM_FORMAT_U18_3BE] = {
|
||||
.width = 18, .phys = 24, .le = 0, .signd = 0,
|
||||
.silence = { 0x02, 0x00, 0x00 },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* snd_pcm_format_signed - Check the PCM format is signed linear
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is signed linear, 0 if unsigned
|
||||
* linear, and a negative error code for non-linear formats.
|
||||
*/
|
||||
int snd_pcm_format_signed(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[format].signd) < 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_unsigned - Check the PCM format is unsigned linear
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is unsigned linear, 0 if signed
|
||||
* linear, and a negative error code for non-linear formats.
|
||||
*/
|
||||
int snd_pcm_format_unsigned(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_pcm_format_signed(format);
|
||||
if (val < 0)
|
||||
return val;
|
||||
return !val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_linear - Check the PCM format is linear
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is linear, 0 if not.
|
||||
*/
|
||||
int snd_pcm_format_linear(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_signed(format) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_little_endian - Check the PCM format is little-endian
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is little-endian, 0 if
|
||||
* big-endian, or a negative error code if endian not specified.
|
||||
*/
|
||||
int snd_pcm_format_little_endian(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[format].le) < 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_big_endian - Check the PCM format is big-endian
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is big-endian, 0 if
|
||||
* little-endian, or a negative error code if endian not specified.
|
||||
*/
|
||||
int snd_pcm_format_big_endian(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = snd_pcm_format_little_endian(format);
|
||||
if (val < 0)
|
||||
return val;
|
||||
return !val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns 1 if the given PCM format is CPU-endian, 0 if
|
||||
* opposite, or a negative error code if endian not specified.
|
||||
*/
|
||||
int snd_pcm_format_cpu_endian(snd_pcm_format_t format)
|
||||
{
|
||||
#ifdef SNDRV_LITTLE_ENDIAN
|
||||
return snd_pcm_format_little_endian(format);
|
||||
#else
|
||||
return snd_pcm_format_big_endian(format);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_width - return the bit-width of the format
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns the bit-width of the format, or a negative error code
|
||||
* if unknown format.
|
||||
*/
|
||||
int snd_pcm_format_width(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[format].width) == 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_physical_width - return the physical bit-width of the format
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns the physical bit-width of the format, or a negative error code
|
||||
* if unknown format.
|
||||
*/
|
||||
int snd_pcm_format_physical_width(snd_pcm_format_t format)
|
||||
{
|
||||
int val;
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[format].phys) == 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_size - return the byte size of samples on the given format
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns the byte size of the given samples for the format, or a
|
||||
* negative error code if unknown format.
|
||||
*/
|
||||
ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
|
||||
{
|
||||
int phys_width = snd_pcm_format_physical_width(format);
|
||||
if (phys_width < 0)
|
||||
return -EINVAL;
|
||||
return samples * phys_width / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
|
||||
* @format: the format to check
|
||||
*
|
||||
* Returns the format pattern to fill or NULL if error.
|
||||
*/
|
||||
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
|
||||
{
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return NULL;
|
||||
if (! pcm_formats[format].phys)
|
||||
return NULL;
|
||||
return pcm_formats[format].silence;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_format_set_silence - set the silence data on the buffer
|
||||
* @format: the PCM format
|
||||
* @data: the buffer pointer
|
||||
* @samples: the number of samples to set silence
|
||||
*
|
||||
* Sets the silence data on the buffer for the given samples.
|
||||
*
|
||||
* Returns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
|
||||
{
|
||||
int width;
|
||||
unsigned char *dst, *pat;
|
||||
|
||||
if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
|
||||
return -EINVAL;
|
||||
if (samples == 0)
|
||||
return 0;
|
||||
width = pcm_formats[format].phys; /* physical width */
|
||||
pat = pcm_formats[format].silence;
|
||||
if (! width)
|
||||
return -EINVAL;
|
||||
/* signed or 1 byte data */
|
||||
if (pcm_formats[format].signd == 1 || width <= 8) {
|
||||
unsigned int bytes = samples * width / 8;
|
||||
memset(data, *pat, bytes);
|
||||
return 0;
|
||||
}
|
||||
/* non-zero samples, fill using a loop */
|
||||
width /= 8;
|
||||
dst = data;
|
||||
#if 0
|
||||
while (samples--) {
|
||||
memcpy(dst, pat, width);
|
||||
dst += width;
|
||||
}
|
||||
#else
|
||||
/* a bit optimization for constant width */
|
||||
switch (width) {
|
||||
case 2:
|
||||
while (samples--) {
|
||||
memcpy(dst, pat, 2);
|
||||
dst += 2;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
while (samples--) {
|
||||
memcpy(dst, pat, 3);
|
||||
dst += 3;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
while (samples--) {
|
||||
memcpy(dst, pat, 4);
|
||||
dst += 4;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
while (samples--) {
|
||||
memcpy(dst, pat, 8);
|
||||
dst += 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* [width][unsigned][bigendian] */
|
||||
static int linear_formats[4][2][2] = {
|
||||
{{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8},
|
||||
{ SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}},
|
||||
{{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE},
|
||||
{SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}},
|
||||
{{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE},
|
||||
{SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}},
|
||||
{{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE},
|
||||
{SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}}
|
||||
};
|
||||
|
||||
/**
|
||||
* snd_pcm_build_linear_format - return the suitable linear format for the given condition
|
||||
* @width: the bit-width
|
||||
* @unsignd: 1 if unsigned, 0 if signed.
|
||||
* @big_endian: 1 if big-endian, 0 if little-endian
|
||||
*
|
||||
* Returns the suitable linear format for the given condition.
|
||||
*/
|
||||
snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian)
|
||||
{
|
||||
if (width & 7)
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
width = (width / 8) - 1;
|
||||
if (width < 0 || width >= 4)
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
return linear_formats[width][!!unsignd][!!big_endian];
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
|
||||
* @runtime: the runtime instance
|
||||
*
|
||||
* Determines the rate_min and rate_max fields from the rates bits of
|
||||
* the given runtime->hw.
|
||||
*
|
||||
* Returns zero if successful.
|
||||
*/
|
||||
int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime)
|
||||
{
|
||||
static unsigned rates[] = {
|
||||
/* ATTENTION: these values depend on the definition in pcm.h! */
|
||||
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
|
||||
64000, 88200, 96000, 176400, 192000
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < (int)ARRAY_SIZE(rates); i++) {
|
||||
if (runtime->hw.rates & (1 << i)) {
|
||||
runtime->hw.rate_min = rates[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) {
|
||||
if (runtime->hw.rates & (1 << i)) {
|
||||
runtime->hw.rate_max = rates[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
3364
sound/core/pcm_native.c
Normal file
3364
sound/core/pcm_native.c
Normal file
File diff suppressed because it is too large
Load Diff
161
sound/core/pcm_timer.c
Normal file
161
sound/core/pcm_timer.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Digital Audio (PCM) abstract layer
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/timer.h>
|
||||
|
||||
/*
|
||||
* Timer functions
|
||||
*/
|
||||
|
||||
/* Greatest common divisor */
|
||||
static unsigned long gcd(unsigned long a, unsigned long b)
|
||||
{
|
||||
unsigned long r;
|
||||
if (a < b) {
|
||||
r = a;
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
while ((r = a % b) != 0) {
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream)
|
||||
{
|
||||
unsigned long rate, mult, fsize, l, post;
|
||||
snd_pcm_runtime_t *runtime = substream->runtime;
|
||||
|
||||
mult = 1000000000;
|
||||
rate = runtime->rate;
|
||||
snd_assert(rate != 0, return);
|
||||
l = gcd(mult, rate);
|
||||
mult /= l;
|
||||
rate /= l;
|
||||
fsize = runtime->period_size;
|
||||
snd_assert(fsize != 0, return);
|
||||
l = gcd(rate, fsize);
|
||||
rate /= l;
|
||||
fsize /= l;
|
||||
post = 1;
|
||||
while ((mult * fsize) / fsize != mult) {
|
||||
mult /= 2;
|
||||
post *= 2;
|
||||
}
|
||||
if (rate == 0) {
|
||||
snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size);
|
||||
runtime->timer_resolution = -1;
|
||||
return;
|
||||
}
|
||||
runtime->timer_resolution = (mult * fsize / rate) * post;
|
||||
}
|
||||
|
||||
static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer)
|
||||
{
|
||||
snd_pcm_substream_t * substream;
|
||||
|
||||
substream = timer->private_data;
|
||||
return substream->runtime ? substream->runtime->timer_resolution : 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_timer_start(snd_timer_t * timer)
|
||||
{
|
||||
unsigned long flags;
|
||||
snd_pcm_substream_t * substream;
|
||||
|
||||
substream = snd_timer_chip(timer);
|
||||
spin_lock_irqsave(&substream->timer_lock, flags);
|
||||
substream->timer_running = 1;
|
||||
spin_unlock_irqrestore(&substream->timer_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_timer_stop(snd_timer_t * timer)
|
||||
{
|
||||
unsigned long flags;
|
||||
snd_pcm_substream_t * substream;
|
||||
|
||||
substream = snd_timer_chip(timer);
|
||||
spin_lock_irqsave(&substream->timer_lock, flags);
|
||||
substream->timer_running = 0;
|
||||
spin_unlock_irqrestore(&substream->timer_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct _snd_timer_hardware snd_pcm_timer =
|
||||
{
|
||||
.flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
|
||||
.resolution = 0,
|
||||
.ticks = 1,
|
||||
.c_resolution = snd_pcm_timer_resolution,
|
||||
.start = snd_pcm_timer_start,
|
||||
.stop = snd_pcm_timer_stop,
|
||||
};
|
||||
|
||||
/*
|
||||
* Init functions
|
||||
*/
|
||||
|
||||
static void snd_pcm_timer_free(snd_timer_t *timer)
|
||||
{
|
||||
snd_pcm_substream_t *substream = timer->private_data;
|
||||
substream->timer = NULL;
|
||||
}
|
||||
|
||||
void snd_pcm_timer_init(snd_pcm_substream_t *substream)
|
||||
{
|
||||
snd_timer_id_t tid;
|
||||
snd_timer_t *timer;
|
||||
|
||||
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
|
||||
tid.dev_class = SNDRV_TIMER_CLASS_PCM;
|
||||
tid.card = substream->pcm->card->number;
|
||||
tid.device = substream->pcm->device;
|
||||
tid.subdevice = (substream->number << 1) | (substream->stream & 1);
|
||||
if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0)
|
||||
return;
|
||||
sprintf(timer->name, "PCM %s %i-%i-%i",
|
||||
substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
|
||||
"capture" : "playback",
|
||||
tid.card, tid.device, tid.subdevice);
|
||||
timer->hw = snd_pcm_timer;
|
||||
if (snd_device_register(timer->card, timer) < 0) {
|
||||
snd_device_free(timer->card, timer);
|
||||
return;
|
||||
}
|
||||
timer->private_data = substream;
|
||||
timer->private_free = snd_pcm_timer_free;
|
||||
substream->timer = timer;
|
||||
}
|
||||
|
||||
void snd_pcm_timer_done(snd_pcm_substream_t *substream)
|
||||
{
|
||||
if (substream->timer) {
|
||||
snd_device_free(substream->pcm->card, substream->timer);
|
||||
substream->timer = NULL;
|
||||
}
|
||||
}
|
||||
1680
sound/core/rawmidi.c
Normal file
1680
sound/core/rawmidi.c
Normal file
File diff suppressed because it is too large
Load Diff
120
sound/core/rawmidi_compat.c
Normal file
120
sound/core/rawmidi_compat.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 32bit -> 64bit ioctl wrapper for raw MIDI API
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file included from rawmidi.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct sndrv_rawmidi_params32 {
|
||||
s32 stream;
|
||||
u32 buffer_size;
|
||||
u32 avail_min;
|
||||
unsigned int no_active_sensing; /* avoid bit-field */
|
||||
unsigned char reserved[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int snd_rawmidi_ioctl_params_compat(snd_rawmidi_file_t *rfile,
|
||||
struct sndrv_rawmidi_params32 __user *src)
|
||||
{
|
||||
snd_rawmidi_params_t params;
|
||||
unsigned int val;
|
||||
|
||||
if (rfile->output == NULL)
|
||||
return -EINVAL;
|
||||
if (get_user(params.stream, &src->stream) ||
|
||||
get_user(params.buffer_size, &src->buffer_size) ||
|
||||
get_user(params.avail_min, &src->avail_min) ||
|
||||
get_user(val, &src->no_active_sensing))
|
||||
return -EFAULT;
|
||||
params.no_active_sensing = val;
|
||||
switch (params.stream) {
|
||||
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
||||
return snd_rawmidi_output_params(rfile->output, ¶ms);
|
||||
case SNDRV_RAWMIDI_STREAM_INPUT:
|
||||
return snd_rawmidi_input_params(rfile->input, ¶ms);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct sndrv_rawmidi_status32 {
|
||||
s32 stream;
|
||||
struct compat_timespec tstamp;
|
||||
u32 avail;
|
||||
u32 xruns;
|
||||
unsigned char reserved[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int snd_rawmidi_ioctl_status_compat(snd_rawmidi_file_t *rfile,
|
||||
struct sndrv_rawmidi_status32 __user *src)
|
||||
{
|
||||
int err;
|
||||
snd_rawmidi_status_t status;
|
||||
|
||||
if (rfile->output == NULL)
|
||||
return -EINVAL;
|
||||
if (get_user(status.stream, &src->stream))
|
||||
return -EFAULT;
|
||||
|
||||
switch (status.stream) {
|
||||
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
||||
err = snd_rawmidi_output_status(rfile->output, &status);
|
||||
break;
|
||||
case SNDRV_RAWMIDI_STREAM_INPUT:
|
||||
err = snd_rawmidi_input_status(rfile->input, &status);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
|
||||
put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
|
||||
put_user(status.avail, &src->avail) ||
|
||||
put_user(status.xruns, &src->xruns))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct sndrv_rawmidi_params32),
|
||||
SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct sndrv_rawmidi_status32),
|
||||
};
|
||||
|
||||
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
snd_rawmidi_file_t *rfile;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
|
||||
rfile = file->private_data;
|
||||
switch (cmd) {
|
||||
case SNDRV_RAWMIDI_IOCTL_PVERSION:
|
||||
case SNDRV_RAWMIDI_IOCTL_INFO:
|
||||
case SNDRV_RAWMIDI_IOCTL_DROP:
|
||||
case SNDRV_RAWMIDI_IOCTL_DRAIN:
|
||||
return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
|
||||
case SNDRV_RAWMIDI_IOCTL_PARAMS32:
|
||||
return snd_rawmidi_ioctl_params_compat(rfile, argp);
|
||||
case SNDRV_RAWMIDI_IOCTL_STATUS32:
|
||||
return snd_rawmidi_ioctl_status_compat(rfile, argp);
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
188
sound/core/rtctimer.c
Normal file
188
sound/core/rtctimer.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* RTC based high-frequency timer
|
||||
*
|
||||
* Copyright (C) 2000 Takashi Iwai
|
||||
* based on rtctimer.c by Steve Ratcliffe
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/timer.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
|
||||
|
||||
#include <linux/mc146818rtc.h>
|
||||
|
||||
#define RTC_FREQ 1024 /* default frequency */
|
||||
#define NANO_SEC 1000000000L /* 10^9 in sec */
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int rtctimer_open(snd_timer_t *t);
|
||||
static int rtctimer_close(snd_timer_t *t);
|
||||
static int rtctimer_start(snd_timer_t *t);
|
||||
static int rtctimer_stop(snd_timer_t *t);
|
||||
|
||||
|
||||
/*
|
||||
* The hardware dependent description for this timer.
|
||||
*/
|
||||
static struct _snd_timer_hardware rtc_hw = {
|
||||
.flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
|
||||
.ticks = 100000000L, /* FIXME: XXX */
|
||||
.open = rtctimer_open,
|
||||
.close = rtctimer_close,
|
||||
.start = rtctimer_start,
|
||||
.stop = rtctimer_stop,
|
||||
};
|
||||
|
||||
static int rtctimer_freq = RTC_FREQ; /* frequency */
|
||||
static snd_timer_t *rtctimer;
|
||||
static atomic_t rtc_inc = ATOMIC_INIT(0);
|
||||
static rtc_task_t rtc_task;
|
||||
|
||||
|
||||
static int
|
||||
rtctimer_open(snd_timer_t *t)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rtc_register(&rtc_task);
|
||||
if (err < 0)
|
||||
return err;
|
||||
t->private_data = &rtc_task;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_close(snd_timer_t *t)
|
||||
{
|
||||
rtc_task_t *rtc = t->private_data;
|
||||
if (rtc) {
|
||||
rtc_unregister(rtc);
|
||||
t->private_data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_start(snd_timer_t *timer)
|
||||
{
|
||||
rtc_task_t *rtc = timer->private_data;
|
||||
snd_assert(rtc != NULL, return -EINVAL);
|
||||
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
|
||||
rtc_control(rtc, RTC_PIE_ON, 0);
|
||||
atomic_set(&rtc_inc, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtctimer_stop(snd_timer_t *timer)
|
||||
{
|
||||
rtc_task_t *rtc = timer->private_data;
|
||||
snd_assert(rtc != NULL, return -EINVAL);
|
||||
rtc_control(rtc, RTC_PIE_OFF, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt
|
||||
*/
|
||||
static void rtctimer_interrupt(void *private_data)
|
||||
{
|
||||
int ticks;
|
||||
|
||||
atomic_inc(&rtc_inc);
|
||||
ticks = atomic_read(&rtc_inc);
|
||||
snd_timer_interrupt((snd_timer_t*)private_data, ticks);
|
||||
atomic_sub(ticks, &rtc_inc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ENTRY functions
|
||||
*/
|
||||
static int __init rtctimer_init(void)
|
||||
{
|
||||
int order, err;
|
||||
snd_timer_t *timer;
|
||||
|
||||
if (rtctimer_freq < 2 || rtctimer_freq > 8192) {
|
||||
snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (order = 1; rtctimer_freq > order; order <<= 1)
|
||||
;
|
||||
if (rtctimer_freq != order) {
|
||||
snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create a new timer and set up the fields */
|
||||
err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
strcpy(timer->name, "RTC timer");
|
||||
timer->hw = rtc_hw;
|
||||
timer->hw.resolution = NANO_SEC / rtctimer_freq;
|
||||
|
||||
/* set up RTC callback */
|
||||
rtc_task.func = rtctimer_interrupt;
|
||||
rtc_task.private_data = timer;
|
||||
|
||||
err = snd_timer_global_register(timer);
|
||||
if (err < 0) {
|
||||
snd_timer_global_free(timer);
|
||||
return err;
|
||||
}
|
||||
rtctimer = timer; /* remember this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rtctimer_exit(void)
|
||||
{
|
||||
if (rtctimer) {
|
||||
snd_timer_global_unregister(rtctimer);
|
||||
rtctimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* exported stuff
|
||||
*/
|
||||
module_init(rtctimer_init)
|
||||
module_exit(rtctimer_exit)
|
||||
|
||||
module_param(rtctimer_freq, int, 0444);
|
||||
MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
|
||||
|
||||
#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
|
||||
44
sound/core/seq/Makefile
Normal file
44
sound/core/seq/Makefile
Normal file
@@ -0,0 +1,44 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SND) += instr/
|
||||
ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
|
||||
obj-$(CONFIG_SND_SEQUENCER) += oss/
|
||||
endif
|
||||
|
||||
snd-seq-device-objs := seq_device.o
|
||||
snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
|
||||
seq_fifo.o seq_prioq.o seq_timer.o \
|
||||
seq_system.o seq_ports.o seq_info.o
|
||||
snd-seq-midi-objs := seq_midi.o
|
||||
snd-seq-midi-emul-objs := seq_midi_emul.o
|
||||
snd-seq-midi-event-objs := seq_midi_event.o
|
||||
snd-seq-instr-objs := seq_instr.o
|
||||
snd-seq-dummy-objs := seq_dummy.o
|
||||
snd-seq-virmidi-objs := seq_virmidi.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
|
||||
ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
|
||||
obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
|
||||
endif
|
||||
obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o
|
||||
23
sound/core/seq/instr/Makefile
Normal file
23
sound/core/seq/instr/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
#
|
||||
|
||||
snd-ainstr-fm-objs := ainstr_fm.o
|
||||
snd-ainstr-simple-objs := ainstr_simple.o
|
||||
snd-ainstr-gf1-objs := ainstr_gf1.o
|
||||
snd-ainstr-iw-objs := ainstr_iw.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o
|
||||
156
sound/core/seq/instr/ainstr_fm.c
Normal file
156
sound/core/seq/instr/ainstr_fm.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* FM (OPL2/3) Instrument routines
|
||||
* Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/ainstr_fm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int snd_seq_fm_put(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic, int cmd)
|
||||
{
|
||||
fm_instrument_t *ip;
|
||||
fm_xinstrument_t ix;
|
||||
int idx;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
|
||||
return -EINVAL;
|
||||
/* copy instrument data */
|
||||
if (len < (long)sizeof(ix))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ix, instr_data, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
if (ix.stype != FM_STRU_INSTR)
|
||||
return -EINVAL;
|
||||
ip = (fm_instrument_t *)KINSTR_DATA(instr);
|
||||
ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
|
||||
ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
|
||||
ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
|
||||
ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
|
||||
ip->type = ix.type;
|
||||
for (idx = 0; idx < 4; idx++) {
|
||||
ip->op[idx].am_vib = ix.op[idx].am_vib;
|
||||
ip->op[idx].ksl_level = ix.op[idx].ksl_level;
|
||||
ip->op[idx].attack_decay = ix.op[idx].attack_decay;
|
||||
ip->op[idx].sustain_release = ix.op[idx].sustain_release;
|
||||
ip->op[idx].wave_select = ix.op[idx].wave_select;
|
||||
}
|
||||
for (idx = 0; idx < 2; idx++) {
|
||||
ip->feedback_connection[idx] = ix.feedback_connection[idx];
|
||||
}
|
||||
ip->echo_delay = ix.echo_delay;
|
||||
ip->echo_atten = ix.echo_atten;
|
||||
ip->chorus_spread = ix.chorus_spread;
|
||||
ip->trnsps = ix.trnsps;
|
||||
ip->fix_dur = ix.fix_dur;
|
||||
ip->modes = ix.modes;
|
||||
ip->fix_key = ix.fix_key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_fm_get(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic,
|
||||
int cmd)
|
||||
{
|
||||
fm_instrument_t *ip;
|
||||
fm_xinstrument_t ix;
|
||||
int idx;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
|
||||
return -EINVAL;
|
||||
if (len < (long)sizeof(ix))
|
||||
return -ENOMEM;
|
||||
memset(&ix, 0, sizeof(ix));
|
||||
ip = (fm_instrument_t *)KINSTR_DATA(instr);
|
||||
ix.stype = FM_STRU_INSTR;
|
||||
ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
|
||||
ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
|
||||
ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
|
||||
ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
|
||||
ix.type = ip->type;
|
||||
for (idx = 0; idx < 4; idx++) {
|
||||
ix.op[idx].am_vib = ip->op[idx].am_vib;
|
||||
ix.op[idx].ksl_level = ip->op[idx].ksl_level;
|
||||
ix.op[idx].attack_decay = ip->op[idx].attack_decay;
|
||||
ix.op[idx].sustain_release = ip->op[idx].sustain_release;
|
||||
ix.op[idx].wave_select = ip->op[idx].wave_select;
|
||||
}
|
||||
for (idx = 0; idx < 2; idx++) {
|
||||
ix.feedback_connection[idx] = ip->feedback_connection[idx];
|
||||
}
|
||||
if (copy_to_user(instr_data, &ix, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
ix.echo_delay = ip->echo_delay;
|
||||
ix.echo_atten = ip->echo_atten;
|
||||
ix.chorus_spread = ip->chorus_spread;
|
||||
ix.trnsps = ip->trnsps;
|
||||
ix.fix_dur = ip->fix_dur;
|
||||
ix.modes = ip->modes;
|
||||
ix.fix_key = ip->fix_key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_fm_get_size(void *private_data, snd_seq_kinstr_t *instr,
|
||||
long *size)
|
||||
{
|
||||
*size = sizeof(fm_xinstrument_t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_fm_init(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_ops_t *next)
|
||||
{
|
||||
memset(ops, 0, sizeof(*ops));
|
||||
// ops->private_data = private_data;
|
||||
ops->add_len = sizeof(fm_instrument_t);
|
||||
ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3;
|
||||
ops->put = snd_seq_fm_put;
|
||||
ops->get = snd_seq_fm_get;
|
||||
ops->get_size = snd_seq_fm_get_size;
|
||||
// ops->remove = snd_seq_fm_remove;
|
||||
// ops->notify = snd_seq_fm_notify;
|
||||
ops->next = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_ainstr_fm_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_ainstr_fm_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_ainstr_fm_init)
|
||||
module_exit(alsa_ainstr_fm_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_fm_init);
|
||||
358
sound/core/seq/instr/ainstr_gf1.c
Normal file
358
sound/core/seq/instr/ainstr_gf1.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* GF1 (GUS) Patch - Instrument routines
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/ainstr_gf1.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format)
|
||||
{
|
||||
unsigned int result = size;
|
||||
|
||||
if (format & GF1_WAVE_16BIT)
|
||||
result <<= 1;
|
||||
if (format & GF1_WAVE_STEREO)
|
||||
result <<= 1;
|
||||
return format;
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops,
|
||||
gf1_instrument_t *ip,
|
||||
char __user **data,
|
||||
long *len,
|
||||
int atomic)
|
||||
{
|
||||
gf1_wave_t *wp, *prev;
|
||||
gf1_xwave_t xp;
|
||||
int err, gfp_mask;
|
||||
unsigned int real_size;
|
||||
|
||||
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
if (*len < (long)sizeof(xp))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&xp, *data, sizeof(xp)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(xp);
|
||||
*len -= sizeof(xp);
|
||||
wp = kcalloc(1, sizeof(*wp), gfp_mask);
|
||||
if (wp == NULL)
|
||||
return -ENOMEM;
|
||||
wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
|
||||
wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
|
||||
wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
|
||||
wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
|
||||
wp->format = le32_to_cpu(xp.format);
|
||||
wp->size = le32_to_cpu(xp.size);
|
||||
wp->start = le32_to_cpu(xp.start);
|
||||
wp->loop_start = le32_to_cpu(xp.loop_start);
|
||||
wp->loop_end = le32_to_cpu(xp.loop_end);
|
||||
wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
|
||||
wp->flags = xp.flags;
|
||||
wp->sample_rate = le32_to_cpu(xp.sample_rate);
|
||||
wp->low_frequency = le32_to_cpu(xp.low_frequency);
|
||||
wp->high_frequency = le32_to_cpu(xp.high_frequency);
|
||||
wp->root_frequency = le32_to_cpu(xp.root_frequency);
|
||||
wp->tune = le16_to_cpu(xp.tune);
|
||||
wp->balance = xp.balance;
|
||||
memcpy(wp->envelope_rate, xp.envelope_rate, 6);
|
||||
memcpy(wp->envelope_offset, xp.envelope_offset, 6);
|
||||
wp->tremolo_sweep = xp.tremolo_sweep;
|
||||
wp->tremolo_rate = xp.tremolo_rate;
|
||||
wp->tremolo_depth = xp.tremolo_depth;
|
||||
wp->vibrato_sweep = xp.vibrato_sweep;
|
||||
wp->vibrato_rate = xp.vibrato_rate;
|
||||
wp->vibrato_depth = xp.vibrato_depth;
|
||||
wp->scale_frequency = le16_to_cpu(xp.scale_frequency);
|
||||
wp->scale_factor = le16_to_cpu(xp.scale_factor);
|
||||
real_size = snd_seq_gf1_size(wp->size, wp->format);
|
||||
if ((long)real_size > *len) {
|
||||
kfree(wp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ops->put_sample) {
|
||||
err = ops->put_sample(ops->private_data, wp,
|
||||
*data, real_size, atomic);
|
||||
if (err < 0) {
|
||||
kfree(wp);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
*data += real_size;
|
||||
*len -= real_size;
|
||||
prev = ip->wave;
|
||||
if (prev) {
|
||||
while (prev->next) prev = prev->next;
|
||||
prev->next = wp;
|
||||
} else {
|
||||
ip->wave = wp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_gf1_wave_free(snd_gf1_ops_t *ops,
|
||||
gf1_wave_t *wave,
|
||||
int atomic)
|
||||
{
|
||||
if (ops->remove_sample)
|
||||
ops->remove_sample(ops->private_data, wave, atomic);
|
||||
kfree(wave);
|
||||
}
|
||||
|
||||
static void snd_seq_gf1_instr_free(snd_gf1_ops_t *ops,
|
||||
gf1_instrument_t *ip,
|
||||
int atomic)
|
||||
{
|
||||
gf1_wave_t *wave;
|
||||
|
||||
while ((wave = ip->wave) != NULL) {
|
||||
ip->wave = wave->next;
|
||||
snd_seq_gf1_wave_free(ops, wave, atomic);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_put(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic,
|
||||
int cmd)
|
||||
{
|
||||
snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
|
||||
gf1_instrument_t *ip;
|
||||
gf1_xinstrument_t ix;
|
||||
int err, gfp_mask;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
|
||||
return -EINVAL;
|
||||
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
/* copy instrument data */
|
||||
if (len < (long)sizeof(ix))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ix, instr_data, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
if (ix.stype != GF1_STRU_INSTR)
|
||||
return -EINVAL;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
ip = (gf1_instrument_t *)KINSTR_DATA(instr);
|
||||
ip->exclusion = le16_to_cpu(ix.exclusion);
|
||||
ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
|
||||
ip->effect1 = ix.effect1;
|
||||
ip->effect1_depth = ix.effect1_depth;
|
||||
ip->effect2 = ix.effect2;
|
||||
ip->effect2_depth = ix.effect2_depth;
|
||||
/* copy layers */
|
||||
while (len > (long)sizeof(__u32)) {
|
||||
__u32 stype;
|
||||
|
||||
if (copy_from_user(&stype, instr_data, sizeof(stype)))
|
||||
return -EFAULT;
|
||||
if (stype != GF1_STRU_WAVE) {
|
||||
snd_seq_gf1_instr_free(ops, ip, atomic);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = snd_seq_gf1_copy_wave_from_stream(ops,
|
||||
ip,
|
||||
&instr_data,
|
||||
&len,
|
||||
atomic);
|
||||
if (err < 0) {
|
||||
snd_seq_gf1_instr_free(ops, ip, atomic);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_copy_wave_to_stream(snd_gf1_ops_t *ops,
|
||||
gf1_instrument_t *ip,
|
||||
char __user **data,
|
||||
long *len,
|
||||
int atomic)
|
||||
{
|
||||
gf1_wave_t *wp;
|
||||
gf1_xwave_t xp;
|
||||
int err;
|
||||
unsigned int real_size;
|
||||
|
||||
for (wp = ip->wave; wp; wp = wp->next) {
|
||||
if (*len < (long)sizeof(xp))
|
||||
return -ENOMEM;
|
||||
memset(&xp, 0, sizeof(xp));
|
||||
xp.stype = GF1_STRU_WAVE;
|
||||
xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
|
||||
xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
|
||||
xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
|
||||
xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
|
||||
xp.format = cpu_to_le32(wp->format);
|
||||
xp.size = cpu_to_le32(wp->size);
|
||||
xp.start = cpu_to_le32(wp->start);
|
||||
xp.loop_start = cpu_to_le32(wp->loop_start);
|
||||
xp.loop_end = cpu_to_le32(wp->loop_end);
|
||||
xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
|
||||
xp.flags = wp->flags;
|
||||
xp.sample_rate = cpu_to_le32(wp->sample_rate);
|
||||
xp.low_frequency = cpu_to_le32(wp->low_frequency);
|
||||
xp.high_frequency = cpu_to_le32(wp->high_frequency);
|
||||
xp.root_frequency = cpu_to_le32(wp->root_frequency);
|
||||
xp.tune = cpu_to_le16(wp->tune);
|
||||
xp.balance = wp->balance;
|
||||
memcpy(xp.envelope_rate, wp->envelope_rate, 6);
|
||||
memcpy(xp.envelope_offset, wp->envelope_offset, 6);
|
||||
xp.tremolo_sweep = wp->tremolo_sweep;
|
||||
xp.tremolo_rate = wp->tremolo_rate;
|
||||
xp.tremolo_depth = wp->tremolo_depth;
|
||||
xp.vibrato_sweep = wp->vibrato_sweep;
|
||||
xp.vibrato_rate = wp->vibrato_rate;
|
||||
xp.vibrato_depth = wp->vibrato_depth;
|
||||
xp.scale_frequency = cpu_to_le16(wp->scale_frequency);
|
||||
xp.scale_factor = cpu_to_le16(wp->scale_factor);
|
||||
if (copy_to_user(*data, &xp, sizeof(xp)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(xp);
|
||||
*len -= sizeof(xp);
|
||||
real_size = snd_seq_gf1_size(wp->size, wp->format);
|
||||
if (*len < (long)real_size)
|
||||
return -ENOMEM;
|
||||
if (ops->get_sample) {
|
||||
err = ops->get_sample(ops->private_data, wp,
|
||||
*data, real_size, atomic);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
*data += wp->size;
|
||||
*len -= wp->size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_get(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic,
|
||||
int cmd)
|
||||
{
|
||||
snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
|
||||
gf1_instrument_t *ip;
|
||||
gf1_xinstrument_t ix;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
|
||||
return -EINVAL;
|
||||
if (len < (long)sizeof(ix))
|
||||
return -ENOMEM;
|
||||
memset(&ix, 0, sizeof(ix));
|
||||
ip = (gf1_instrument_t *)KINSTR_DATA(instr);
|
||||
ix.stype = GF1_STRU_INSTR;
|
||||
ix.exclusion = cpu_to_le16(ip->exclusion);
|
||||
ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
|
||||
ix.effect1 = cpu_to_le16(ip->effect1);
|
||||
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
|
||||
ix.effect2 = ip->effect2;
|
||||
ix.effect2_depth = ip->effect2_depth;
|
||||
if (copy_to_user(instr_data, &ix, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
return snd_seq_gf1_copy_wave_to_stream(ops,
|
||||
ip,
|
||||
&instr_data,
|
||||
&len,
|
||||
atomic);
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_get_size(void *private_data, snd_seq_kinstr_t *instr,
|
||||
long *size)
|
||||
{
|
||||
long result;
|
||||
gf1_instrument_t *ip;
|
||||
gf1_wave_t *wp;
|
||||
|
||||
*size = 0;
|
||||
ip = (gf1_instrument_t *)KINSTR_DATA(instr);
|
||||
result = sizeof(gf1_xinstrument_t);
|
||||
for (wp = ip->wave; wp; wp = wp->next) {
|
||||
result += sizeof(gf1_xwave_t);
|
||||
result += wp->size;
|
||||
}
|
||||
*size = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_gf1_remove(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int atomic)
|
||||
{
|
||||
snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
|
||||
gf1_instrument_t *ip;
|
||||
|
||||
ip = (gf1_instrument_t *)KINSTR_DATA(instr);
|
||||
snd_seq_gf1_instr_free(ops, ip, atomic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_gf1_notify(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int what)
|
||||
{
|
||||
snd_gf1_ops_t *ops = (snd_gf1_ops_t *)private_data;
|
||||
|
||||
if (ops->notify)
|
||||
ops->notify(ops->private_data, instr, what);
|
||||
}
|
||||
|
||||
int snd_seq_gf1_init(snd_gf1_ops_t *ops,
|
||||
void *private_data,
|
||||
snd_seq_kinstr_ops_t *next)
|
||||
{
|
||||
memset(ops, 0, sizeof(*ops));
|
||||
ops->private_data = private_data;
|
||||
ops->kops.private_data = ops;
|
||||
ops->kops.add_len = sizeof(gf1_instrument_t);
|
||||
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH;
|
||||
ops->kops.put = snd_seq_gf1_put;
|
||||
ops->kops.get = snd_seq_gf1_get;
|
||||
ops->kops.get_size = snd_seq_gf1_get_size;
|
||||
ops->kops.remove = snd_seq_gf1_remove;
|
||||
ops->kops.notify = snd_seq_gf1_notify;
|
||||
ops->kops.next = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_ainstr_gf1_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_ainstr_gf1_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_ainstr_gf1_init)
|
||||
module_exit(alsa_ainstr_gf1_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_gf1_init);
|
||||
622
sound/core/seq/instr/ainstr_iw.c
Normal file
622
sound/core/seq/instr/ainstr_iw.c
Normal file
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* IWFFFF - AMD InterWave (tm) - Instrument routines
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/ainstr_iw.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format)
|
||||
{
|
||||
unsigned int result = size;
|
||||
|
||||
if (format & IWFFFF_WAVE_16BIT)
|
||||
result <<= 1;
|
||||
if (format & IWFFFF_WAVE_STEREO)
|
||||
result <<= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_copy_lfo_from_stream(iwffff_lfo_t *fp,
|
||||
iwffff_xlfo_t *fx)
|
||||
{
|
||||
fp->freq = le16_to_cpu(fx->freq);
|
||||
fp->depth = le16_to_cpu(fx->depth);
|
||||
fp->sweep = le16_to_cpu(fx->sweep);
|
||||
fp->shape = fx->shape;
|
||||
fp->delay = fx->delay;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype,
|
||||
iwffff_layer_t *lp,
|
||||
iwffff_env_t *ep,
|
||||
iwffff_xenv_t *ex,
|
||||
char __user **data,
|
||||
long *len,
|
||||
int gfp_mask)
|
||||
{
|
||||
__u32 stype;
|
||||
iwffff_env_record_t *rp, *rp_last;
|
||||
iwffff_xenv_record_t rx;
|
||||
iwffff_env_point_t *pp;
|
||||
iwffff_xenv_point_t px;
|
||||
int points_size, idx;
|
||||
|
||||
ep->flags = ex->flags;
|
||||
ep->mode = ex->mode;
|
||||
ep->index = ex->index;
|
||||
rp_last = NULL;
|
||||
while (1) {
|
||||
if (*len < (long)sizeof(__u32))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&stype, *data, sizeof(stype)))
|
||||
return -EFAULT;
|
||||
if (stype == IWFFFF_STRU_WAVE)
|
||||
return 0;
|
||||
if (req_stype != stype) {
|
||||
if (stype == IWFFFF_STRU_ENV_RECP ||
|
||||
stype == IWFFFF_STRU_ENV_RECV)
|
||||
return 0;
|
||||
}
|
||||
if (*len < (long)sizeof(rx))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&rx, *data, sizeof(rx)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(rx);
|
||||
*len -= sizeof(rx);
|
||||
points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16);
|
||||
if (points_size > *len)
|
||||
return -EINVAL;
|
||||
rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask);
|
||||
if (rp == NULL)
|
||||
return -ENOMEM;
|
||||
rp->nattack = le16_to_cpu(rx.nattack);
|
||||
rp->nrelease = le16_to_cpu(rx.nrelease);
|
||||
rp->sustain_offset = le16_to_cpu(rx.sustain_offset);
|
||||
rp->sustain_rate = le16_to_cpu(rx.sustain_rate);
|
||||
rp->release_rate = le16_to_cpu(rx.release_rate);
|
||||
rp->hirange = rx.hirange;
|
||||
pp = (iwffff_env_point_t *)(rp + 1);
|
||||
for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
|
||||
if (copy_from_user(&px, *data, sizeof(px)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(px);
|
||||
*len -= sizeof(px);
|
||||
pp->offset = le16_to_cpu(px.offset);
|
||||
pp->rate = le16_to_cpu(px.rate);
|
||||
}
|
||||
if (ep->record == NULL) {
|
||||
ep->record = rp;
|
||||
} else {
|
||||
rp_last = rp;
|
||||
}
|
||||
rp_last = rp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops,
|
||||
iwffff_layer_t *lp,
|
||||
char __user **data,
|
||||
long *len,
|
||||
int atomic)
|
||||
{
|
||||
iwffff_wave_t *wp, *prev;
|
||||
iwffff_xwave_t xp;
|
||||
int err, gfp_mask;
|
||||
unsigned int real_size;
|
||||
|
||||
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
if (*len < (long)sizeof(xp))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&xp, *data, sizeof(xp)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(xp);
|
||||
*len -= sizeof(xp);
|
||||
wp = kcalloc(1, sizeof(*wp), gfp_mask);
|
||||
if (wp == NULL)
|
||||
return -ENOMEM;
|
||||
wp->share_id[0] = le32_to_cpu(xp.share_id[0]);
|
||||
wp->share_id[1] = le32_to_cpu(xp.share_id[1]);
|
||||
wp->share_id[2] = le32_to_cpu(xp.share_id[2]);
|
||||
wp->share_id[3] = le32_to_cpu(xp.share_id[3]);
|
||||
wp->format = le32_to_cpu(xp.format);
|
||||
wp->address.memory = le32_to_cpu(xp.offset);
|
||||
wp->size = le32_to_cpu(xp.size);
|
||||
wp->start = le32_to_cpu(xp.start);
|
||||
wp->loop_start = le32_to_cpu(xp.loop_start);
|
||||
wp->loop_end = le32_to_cpu(xp.loop_end);
|
||||
wp->loop_repeat = le16_to_cpu(xp.loop_repeat);
|
||||
wp->sample_ratio = le32_to_cpu(xp.sample_ratio);
|
||||
wp->attenuation = xp.attenuation;
|
||||
wp->low_note = xp.low_note;
|
||||
wp->high_note = xp.high_note;
|
||||
real_size = snd_seq_iwffff_size(wp->size, wp->format);
|
||||
if (!(wp->format & IWFFFF_WAVE_ROM)) {
|
||||
if ((long)real_size > *len) {
|
||||
kfree(wp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (ops->put_sample) {
|
||||
err = ops->put_sample(ops->private_data, wp,
|
||||
*data, real_size, atomic);
|
||||
if (err < 0) {
|
||||
kfree(wp);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (!(wp->format & IWFFFF_WAVE_ROM)) {
|
||||
*data += real_size;
|
||||
*len -= real_size;
|
||||
}
|
||||
prev = lp->wave;
|
||||
if (prev) {
|
||||
while (prev->next) prev = prev->next;
|
||||
prev->next = wp;
|
||||
} else {
|
||||
lp->wave = wp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_env_free(snd_iwffff_ops_t *ops,
|
||||
iwffff_env_t *env,
|
||||
int atomic)
|
||||
{
|
||||
iwffff_env_record_t *rec;
|
||||
|
||||
while ((rec = env->record) != NULL) {
|
||||
env->record = rec->next;
|
||||
kfree(rec);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_wave_free(snd_iwffff_ops_t *ops,
|
||||
iwffff_wave_t *wave,
|
||||
int atomic)
|
||||
{
|
||||
if (ops->remove_sample)
|
||||
ops->remove_sample(ops->private_data, wave, atomic);
|
||||
kfree(wave);
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_instr_free(snd_iwffff_ops_t *ops,
|
||||
iwffff_instrument_t *ip,
|
||||
int atomic)
|
||||
{
|
||||
iwffff_layer_t *layer;
|
||||
iwffff_wave_t *wave;
|
||||
|
||||
while ((layer = ip->layer) != NULL) {
|
||||
ip->layer = layer->next;
|
||||
snd_seq_iwffff_env_free(ops, &layer->penv, atomic);
|
||||
snd_seq_iwffff_env_free(ops, &layer->venv, atomic);
|
||||
while ((wave = layer->wave) != NULL) {
|
||||
layer->wave = wave->next;
|
||||
snd_seq_iwffff_wave_free(ops, wave, atomic);
|
||||
}
|
||||
kfree(layer);
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic,
|
||||
int cmd)
|
||||
{
|
||||
snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
|
||||
iwffff_instrument_t *ip;
|
||||
iwffff_xinstrument_t ix;
|
||||
iwffff_layer_t *lp, *prev_lp;
|
||||
iwffff_xlayer_t lx;
|
||||
int err, gfp_mask;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
|
||||
return -EINVAL;
|
||||
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
/* copy instrument data */
|
||||
if (len < (long)sizeof(ix))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ix, instr_data, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
if (ix.stype != IWFFFF_STRU_INSTR)
|
||||
return -EINVAL;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
|
||||
ip->exclusion = le16_to_cpu(ix.exclusion);
|
||||
ip->layer_type = le16_to_cpu(ix.layer_type);
|
||||
ip->exclusion_group = le16_to_cpu(ix.exclusion_group);
|
||||
ip->effect1 = ix.effect1;
|
||||
ip->effect1_depth = ix.effect1_depth;
|
||||
ip->effect2 = ix.effect2;
|
||||
ip->effect2_depth = ix.effect2_depth;
|
||||
/* copy layers */
|
||||
prev_lp = NULL;
|
||||
while (len > 0) {
|
||||
if (len < (long)sizeof(iwffff_xlayer_t)) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (copy_from_user(&lx, instr_data, sizeof(lx)))
|
||||
return -EFAULT;
|
||||
instr_data += sizeof(lx);
|
||||
len -= sizeof(lx);
|
||||
if (lx.stype != IWFFFF_STRU_LAYER) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return -EINVAL;
|
||||
}
|
||||
lp = kcalloc(1, sizeof(*lp), gfp_mask);
|
||||
if (lp == NULL) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (prev_lp) {
|
||||
prev_lp->next = lp;
|
||||
} else {
|
||||
ip->layer = lp;
|
||||
}
|
||||
prev_lp = lp;
|
||||
lp->flags = lx.flags;
|
||||
lp->velocity_mode = lx.velocity_mode;
|
||||
lp->layer_event = lx.layer_event;
|
||||
lp->low_range = lx.low_range;
|
||||
lp->high_range = lx.high_range;
|
||||
lp->pan = lx.pan;
|
||||
lp->pan_freq_scale = lx.pan_freq_scale;
|
||||
lp->attenuation = lx.attenuation;
|
||||
snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo);
|
||||
snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato);
|
||||
lp->freq_scale = le16_to_cpu(lx.freq_scale);
|
||||
lp->freq_center = lx.freq_center;
|
||||
err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP,
|
||||
lp,
|
||||
&lp->penv, &lx.penv,
|
||||
&instr_data, &len,
|
||||
gfp_mask);
|
||||
if (err < 0) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return err;
|
||||
}
|
||||
err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV,
|
||||
lp,
|
||||
&lp->venv, &lx.venv,
|
||||
&instr_data, &len,
|
||||
gfp_mask);
|
||||
if (err < 0) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return err;
|
||||
}
|
||||
while (len > (long)sizeof(__u32)) {
|
||||
__u32 stype;
|
||||
|
||||
if (copy_from_user(&stype, instr_data, sizeof(stype)))
|
||||
return -EFAULT;
|
||||
if (stype != IWFFFF_STRU_WAVE)
|
||||
break;
|
||||
err = snd_seq_iwffff_copy_wave_from_stream(ops,
|
||||
lp,
|
||||
&instr_data,
|
||||
&len,
|
||||
atomic);
|
||||
if (err < 0) {
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_copy_lfo_to_stream(iwffff_xlfo_t *fx,
|
||||
iwffff_lfo_t *fp)
|
||||
{
|
||||
fx->freq = cpu_to_le16(fp->freq);
|
||||
fx->depth = cpu_to_le16(fp->depth);
|
||||
fx->sweep = cpu_to_le16(fp->sweep);
|
||||
fp->shape = fx->shape;
|
||||
fp->delay = fx->delay;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype,
|
||||
iwffff_layer_t *lp,
|
||||
iwffff_xenv_t *ex,
|
||||
iwffff_env_t *ep,
|
||||
char __user **data,
|
||||
long *len)
|
||||
{
|
||||
iwffff_env_record_t *rp;
|
||||
iwffff_xenv_record_t rx;
|
||||
iwffff_env_point_t *pp;
|
||||
iwffff_xenv_point_t px;
|
||||
int points_size, idx;
|
||||
|
||||
ex->flags = ep->flags;
|
||||
ex->mode = ep->mode;
|
||||
ex->index = ep->index;
|
||||
for (rp = ep->record; rp; rp = rp->next) {
|
||||
if (*len < (long)sizeof(rx))
|
||||
return -ENOMEM;
|
||||
memset(&rx, 0, sizeof(rx));
|
||||
rx.stype = req_stype;
|
||||
rx.nattack = cpu_to_le16(rp->nattack);
|
||||
rx.nrelease = cpu_to_le16(rp->nrelease);
|
||||
rx.sustain_offset = cpu_to_le16(rp->sustain_offset);
|
||||
rx.sustain_rate = cpu_to_le16(rp->sustain_rate);
|
||||
rx.release_rate = cpu_to_le16(rp->release_rate);
|
||||
rx.hirange = cpu_to_le16(rp->hirange);
|
||||
if (copy_to_user(*data, &rx, sizeof(rx)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(rx);
|
||||
*len -= sizeof(rx);
|
||||
points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
|
||||
if (*len < points_size)
|
||||
return -ENOMEM;
|
||||
pp = (iwffff_env_point_t *)(rp + 1);
|
||||
for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) {
|
||||
px.offset = cpu_to_le16(pp->offset);
|
||||
px.rate = cpu_to_le16(pp->rate);
|
||||
if (copy_to_user(*data, &px, sizeof(px)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(px);
|
||||
*len -= sizeof(px);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_copy_wave_to_stream(snd_iwffff_ops_t *ops,
|
||||
iwffff_layer_t *lp,
|
||||
char __user **data,
|
||||
long *len,
|
||||
int atomic)
|
||||
{
|
||||
iwffff_wave_t *wp;
|
||||
iwffff_xwave_t xp;
|
||||
int err;
|
||||
unsigned int real_size;
|
||||
|
||||
for (wp = lp->wave; wp; wp = wp->next) {
|
||||
if (*len < (long)sizeof(xp))
|
||||
return -ENOMEM;
|
||||
memset(&xp, 0, sizeof(xp));
|
||||
xp.stype = IWFFFF_STRU_WAVE;
|
||||
xp.share_id[0] = cpu_to_le32(wp->share_id[0]);
|
||||
xp.share_id[1] = cpu_to_le32(wp->share_id[1]);
|
||||
xp.share_id[2] = cpu_to_le32(wp->share_id[2]);
|
||||
xp.share_id[3] = cpu_to_le32(wp->share_id[3]);
|
||||
xp.format = cpu_to_le32(wp->format);
|
||||
if (wp->format & IWFFFF_WAVE_ROM)
|
||||
xp.offset = cpu_to_le32(wp->address.memory);
|
||||
xp.size = cpu_to_le32(wp->size);
|
||||
xp.start = cpu_to_le32(wp->start);
|
||||
xp.loop_start = cpu_to_le32(wp->loop_start);
|
||||
xp.loop_end = cpu_to_le32(wp->loop_end);
|
||||
xp.loop_repeat = cpu_to_le32(wp->loop_repeat);
|
||||
xp.sample_ratio = cpu_to_le32(wp->sample_ratio);
|
||||
xp.attenuation = wp->attenuation;
|
||||
xp.low_note = wp->low_note;
|
||||
xp.high_note = wp->high_note;
|
||||
if (copy_to_user(*data, &xp, sizeof(xp)))
|
||||
return -EFAULT;
|
||||
*data += sizeof(xp);
|
||||
*len -= sizeof(xp);
|
||||
real_size = snd_seq_iwffff_size(wp->size, wp->format);
|
||||
if (!(wp->format & IWFFFF_WAVE_ROM)) {
|
||||
if (*len < (long)real_size)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ops->get_sample) {
|
||||
err = ops->get_sample(ops->private_data, wp,
|
||||
*data, real_size, atomic);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (!(wp->format & IWFFFF_WAVE_ROM)) {
|
||||
*data += real_size;
|
||||
*len -= real_size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_get(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len, int atomic, int cmd)
|
||||
{
|
||||
snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
|
||||
iwffff_instrument_t *ip;
|
||||
iwffff_xinstrument_t ix;
|
||||
iwffff_layer_t *lp;
|
||||
iwffff_xlayer_t lx;
|
||||
char __user *layer_instr_data;
|
||||
int err;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
|
||||
return -EINVAL;
|
||||
if (len < (long)sizeof(ix))
|
||||
return -ENOMEM;
|
||||
memset(&ix, 0, sizeof(ix));
|
||||
ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
|
||||
ix.stype = IWFFFF_STRU_INSTR;
|
||||
ix.exclusion = cpu_to_le16(ip->exclusion);
|
||||
ix.layer_type = cpu_to_le16(ip->layer_type);
|
||||
ix.exclusion_group = cpu_to_le16(ip->exclusion_group);
|
||||
ix.effect1 = cpu_to_le16(ip->effect1);
|
||||
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
|
||||
ix.effect2 = ip->effect2;
|
||||
ix.effect2_depth = ip->effect2_depth;
|
||||
if (copy_to_user(instr_data, &ix, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
for (lp = ip->layer; lp; lp = lp->next) {
|
||||
if (len < (long)sizeof(lx))
|
||||
return -ENOMEM;
|
||||
memset(&lx, 0, sizeof(lx));
|
||||
lx.stype = IWFFFF_STRU_LAYER;
|
||||
lx.flags = lp->flags;
|
||||
lx.velocity_mode = lp->velocity_mode;
|
||||
lx.layer_event = lp->layer_event;
|
||||
lx.low_range = lp->low_range;
|
||||
lx.high_range = lp->high_range;
|
||||
lx.pan = lp->pan;
|
||||
lx.pan_freq_scale = lp->pan_freq_scale;
|
||||
lx.attenuation = lp->attenuation;
|
||||
snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo);
|
||||
snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato);
|
||||
layer_instr_data = instr_data;
|
||||
instr_data += sizeof(lx);
|
||||
len -= sizeof(lx);
|
||||
err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP,
|
||||
lp,
|
||||
&lx.penv, &lp->penv,
|
||||
&instr_data, &len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV,
|
||||
lp,
|
||||
&lx.venv, &lp->venv,
|
||||
&instr_data, &len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* layer structure updating is now finished */
|
||||
if (copy_to_user(layer_instr_data, &lx, sizeof(lx)))
|
||||
return -EFAULT;
|
||||
err = snd_seq_iwffff_copy_wave_to_stream(ops,
|
||||
lp,
|
||||
&instr_data,
|
||||
&len,
|
||||
atomic);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long snd_seq_iwffff_env_size_in_stream(iwffff_env_t *ep)
|
||||
{
|
||||
long result = 0;
|
||||
iwffff_env_record_t *rp;
|
||||
|
||||
for (rp = ep->record; rp; rp = rp->next) {
|
||||
result += sizeof(iwffff_xenv_record_t);
|
||||
result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long snd_seq_iwffff_wave_size_in_stream(iwffff_layer_t *lp)
|
||||
{
|
||||
long result = 0;
|
||||
iwffff_wave_t *wp;
|
||||
|
||||
for (wp = lp->wave; wp; wp = wp->next) {
|
||||
result += sizeof(iwffff_xwave_t);
|
||||
if (!(wp->format & IWFFFF_WAVE_ROM))
|
||||
result += wp->size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_get_size(void *private_data, snd_seq_kinstr_t *instr,
|
||||
long *size)
|
||||
{
|
||||
long result;
|
||||
iwffff_instrument_t *ip;
|
||||
iwffff_layer_t *lp;
|
||||
|
||||
*size = 0;
|
||||
ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
|
||||
result = sizeof(iwffff_xinstrument_t);
|
||||
for (lp = ip->layer; lp; lp = lp->next) {
|
||||
result += sizeof(iwffff_xlayer_t);
|
||||
result += snd_seq_iwffff_env_size_in_stream(&lp->penv);
|
||||
result += snd_seq_iwffff_env_size_in_stream(&lp->venv);
|
||||
result += snd_seq_iwffff_wave_size_in_stream(lp);
|
||||
}
|
||||
*size = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_iwffff_remove(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int atomic)
|
||||
{
|
||||
snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
|
||||
iwffff_instrument_t *ip;
|
||||
|
||||
ip = (iwffff_instrument_t *)KINSTR_DATA(instr);
|
||||
snd_seq_iwffff_instr_free(ops, ip, atomic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_iwffff_notify(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int what)
|
||||
{
|
||||
snd_iwffff_ops_t *ops = (snd_iwffff_ops_t *)private_data;
|
||||
|
||||
if (ops->notify)
|
||||
ops->notify(ops->private_data, instr, what);
|
||||
}
|
||||
|
||||
int snd_seq_iwffff_init(snd_iwffff_ops_t *ops,
|
||||
void *private_data,
|
||||
snd_seq_kinstr_ops_t *next)
|
||||
{
|
||||
memset(ops, 0, sizeof(*ops));
|
||||
ops->private_data = private_data;
|
||||
ops->kops.private_data = ops;
|
||||
ops->kops.add_len = sizeof(iwffff_instrument_t);
|
||||
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE;
|
||||
ops->kops.put = snd_seq_iwffff_put;
|
||||
ops->kops.get = snd_seq_iwffff_get;
|
||||
ops->kops.get_size = snd_seq_iwffff_get_size;
|
||||
ops->kops.remove = snd_seq_iwffff_remove;
|
||||
ops->kops.notify = snd_seq_iwffff_notify;
|
||||
ops->kops.next = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_ainstr_iw_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_ainstr_iw_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_ainstr_iw_init)
|
||||
module_exit(alsa_ainstr_iw_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_iwffff_init);
|
||||
215
sound/core/seq/instr/ainstr_simple.c
Normal file
215
sound/core/seq/instr/ainstr_simple.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Simple (MOD player) - Instrument routines
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/ainstr_simple.h>
|
||||
#include <sound/initval.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format)
|
||||
{
|
||||
unsigned int result = size;
|
||||
|
||||
if (format & SIMPLE_WAVE_16BIT)
|
||||
result <<= 1;
|
||||
if (format & SIMPLE_WAVE_STEREO)
|
||||
result <<= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void snd_seq_simple_instr_free(snd_simple_ops_t *ops,
|
||||
simple_instrument_t *ip,
|
||||
int atomic)
|
||||
{
|
||||
if (ops->remove_sample)
|
||||
ops->remove_sample(ops->private_data, ip, atomic);
|
||||
}
|
||||
|
||||
static int snd_seq_simple_put(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len,
|
||||
int atomic, int cmd)
|
||||
{
|
||||
snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
|
||||
simple_instrument_t *ip;
|
||||
simple_xinstrument_t ix;
|
||||
int err, gfp_mask;
|
||||
unsigned int real_size;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE)
|
||||
return -EINVAL;
|
||||
gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
/* copy instrument data */
|
||||
if (len < (long)sizeof(ix))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ix, instr_data, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
if (ix.stype != SIMPLE_STRU_INSTR)
|
||||
return -EINVAL;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
ip = (simple_instrument_t *)KINSTR_DATA(instr);
|
||||
ip->share_id[0] = le32_to_cpu(ix.share_id[0]);
|
||||
ip->share_id[1] = le32_to_cpu(ix.share_id[1]);
|
||||
ip->share_id[2] = le32_to_cpu(ix.share_id[2]);
|
||||
ip->share_id[3] = le32_to_cpu(ix.share_id[3]);
|
||||
ip->format = le32_to_cpu(ix.format);
|
||||
ip->size = le32_to_cpu(ix.size);
|
||||
ip->start = le32_to_cpu(ix.start);
|
||||
ip->loop_start = le32_to_cpu(ix.loop_start);
|
||||
ip->loop_end = le32_to_cpu(ix.loop_end);
|
||||
ip->loop_repeat = le16_to_cpu(ix.loop_repeat);
|
||||
ip->effect1 = ix.effect1;
|
||||
ip->effect1_depth = ix.effect1_depth;
|
||||
ip->effect2 = ix.effect2;
|
||||
ip->effect2_depth = ix.effect2_depth;
|
||||
real_size = snd_seq_simple_size(ip->size, ip->format);
|
||||
if (len < (long)real_size)
|
||||
return -EINVAL;
|
||||
if (ops->put_sample) {
|
||||
err = ops->put_sample(ops->private_data, ip,
|
||||
instr_data, real_size, atomic);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_simple_get(void *private_data, snd_seq_kinstr_t *instr,
|
||||
char __user *instr_data, long len,
|
||||
int atomic, int cmd)
|
||||
{
|
||||
snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
|
||||
simple_instrument_t *ip;
|
||||
simple_xinstrument_t ix;
|
||||
int err;
|
||||
unsigned int real_size;
|
||||
|
||||
if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL)
|
||||
return -EINVAL;
|
||||
if (len < (long)sizeof(ix))
|
||||
return -ENOMEM;
|
||||
memset(&ix, 0, sizeof(ix));
|
||||
ip = (simple_instrument_t *)KINSTR_DATA(instr);
|
||||
ix.stype = SIMPLE_STRU_INSTR;
|
||||
ix.share_id[0] = cpu_to_le32(ip->share_id[0]);
|
||||
ix.share_id[1] = cpu_to_le32(ip->share_id[1]);
|
||||
ix.share_id[2] = cpu_to_le32(ip->share_id[2]);
|
||||
ix.share_id[3] = cpu_to_le32(ip->share_id[3]);
|
||||
ix.format = cpu_to_le32(ip->format);
|
||||
ix.size = cpu_to_le32(ip->size);
|
||||
ix.start = cpu_to_le32(ip->start);
|
||||
ix.loop_start = cpu_to_le32(ip->loop_start);
|
||||
ix.loop_end = cpu_to_le32(ip->loop_end);
|
||||
ix.loop_repeat = cpu_to_le32(ip->loop_repeat);
|
||||
ix.effect1 = cpu_to_le16(ip->effect1);
|
||||
ix.effect1_depth = cpu_to_le16(ip->effect1_depth);
|
||||
ix.effect2 = ip->effect2;
|
||||
ix.effect2_depth = ip->effect2_depth;
|
||||
if (copy_to_user(instr_data, &ix, sizeof(ix)))
|
||||
return -EFAULT;
|
||||
instr_data += sizeof(ix);
|
||||
len -= sizeof(ix);
|
||||
real_size = snd_seq_simple_size(ip->size, ip->format);
|
||||
if (len < (long)real_size)
|
||||
return -ENOMEM;
|
||||
if (ops->get_sample) {
|
||||
err = ops->get_sample(ops->private_data, ip,
|
||||
instr_data, real_size, atomic);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_simple_get_size(void *private_data, snd_seq_kinstr_t *instr,
|
||||
long *size)
|
||||
{
|
||||
simple_instrument_t *ip;
|
||||
|
||||
ip = (simple_instrument_t *)KINSTR_DATA(instr);
|
||||
*size = sizeof(simple_xinstrument_t) + snd_seq_simple_size(ip->size, ip->format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_simple_remove(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int atomic)
|
||||
{
|
||||
snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
|
||||
simple_instrument_t *ip;
|
||||
|
||||
ip = (simple_instrument_t *)KINSTR_DATA(instr);
|
||||
snd_seq_simple_instr_free(ops, ip, atomic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_seq_simple_notify(void *private_data,
|
||||
snd_seq_kinstr_t *instr,
|
||||
int what)
|
||||
{
|
||||
snd_simple_ops_t *ops = (snd_simple_ops_t *)private_data;
|
||||
|
||||
if (ops->notify)
|
||||
ops->notify(ops->private_data, instr, what);
|
||||
}
|
||||
|
||||
int snd_seq_simple_init(snd_simple_ops_t *ops,
|
||||
void *private_data,
|
||||
snd_seq_kinstr_ops_t *next)
|
||||
{
|
||||
memset(ops, 0, sizeof(*ops));
|
||||
ops->private_data = private_data;
|
||||
ops->kops.private_data = ops;
|
||||
ops->kops.add_len = sizeof(simple_instrument_t);
|
||||
ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE;
|
||||
ops->kops.put = snd_seq_simple_put;
|
||||
ops->kops.get = snd_seq_simple_get;
|
||||
ops->kops.get_size = snd_seq_simple_get_size;
|
||||
ops->kops.remove = snd_seq_simple_remove;
|
||||
ops->kops.notify = snd_seq_simple_notify;
|
||||
ops->kops.next = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_ainstr_simple_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_ainstr_simple_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_ainstr_simple_init)
|
||||
module_exit(alsa_ainstr_simple_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_simple_init);
|
||||
10
sound/core/seq/oss/Makefile
Normal file
10
sound/core/seq/oss/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
#
|
||||
|
||||
snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
|
||||
seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
|
||||
seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
|
||||
|
||||
obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o
|
||||
317
sound/core/seq/oss/seq_oss.c
Normal file
317
sound/core/seq/oss/seq_oss.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* registration of device and proc
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/initval.h>
|
||||
#include "seq_oss_device.h"
|
||||
#include "seq_oss_synth.h"
|
||||
|
||||
/*
|
||||
* module option
|
||||
*/
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("OSS-compatible sequencer module");
|
||||
MODULE_LICENSE("GPL");
|
||||
/* Takashi says this is really only for sound-service-0-, but this is OK. */
|
||||
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
|
||||
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
|
||||
|
||||
#ifdef SNDRV_SEQ_OSS_DEBUG
|
||||
module_param(seq_oss_debug, int, 0644);
|
||||
MODULE_PARM_DESC(seq_oss_debug, "debug option");
|
||||
int seq_oss_debug = 0;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int register_device(void);
|
||||
static void unregister_device(void);
|
||||
static int register_proc(void);
|
||||
static void unregister_proc(void);
|
||||
|
||||
static int odev_open(struct inode *inode, struct file *file);
|
||||
static int odev_release(struct inode *inode, struct file *file);
|
||||
static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
|
||||
static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
|
||||
static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
static unsigned int odev_poll(struct file *file, poll_table * wait);
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* module interface
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_oss_init(void)
|
||||
{
|
||||
int rc;
|
||||
static snd_seq_dev_ops_t ops = {
|
||||
snd_seq_oss_synth_register,
|
||||
snd_seq_oss_synth_unregister,
|
||||
};
|
||||
|
||||
snd_seq_autoload_lock();
|
||||
if ((rc = register_device()) < 0)
|
||||
goto error;
|
||||
if ((rc = register_proc()) < 0) {
|
||||
unregister_device();
|
||||
goto error;
|
||||
}
|
||||
if ((rc = snd_seq_oss_create_client()) < 0) {
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
|
||||
sizeof(snd_seq_oss_reg_t))) < 0) {
|
||||
snd_seq_oss_delete_client();
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* success */
|
||||
snd_seq_oss_synth_init();
|
||||
|
||||
error:
|
||||
snd_seq_autoload_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_oss_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
|
||||
snd_seq_oss_delete_client();
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
}
|
||||
|
||||
module_init(alsa_seq_oss_init)
|
||||
module_exit(alsa_seq_oss_exit)
|
||||
|
||||
/*
|
||||
* ALSA minor device interface
|
||||
*/
|
||||
|
||||
static DECLARE_MUTEX(register_mutex);
|
||||
|
||||
static int
|
||||
odev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int level, rc;
|
||||
|
||||
if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
|
||||
level = SNDRV_SEQ_OSS_MODE_MUSIC;
|
||||
else
|
||||
level = SNDRV_SEQ_OSS_MODE_SYNTH;
|
||||
|
||||
down(®ister_mutex);
|
||||
rc = snd_seq_oss_open(file, level);
|
||||
up(®ister_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
odev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
seq_oss_devinfo_t *dp;
|
||||
|
||||
if ((dp = file->private_data) == NULL)
|
||||
return 0;
|
||||
|
||||
snd_seq_oss_drain_write(dp);
|
||||
|
||||
down(®ister_mutex);
|
||||
snd_seq_oss_release(dp);
|
||||
up(®ister_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
seq_oss_devinfo_t *dp;
|
||||
dp = file->private_data;
|
||||
snd_assert(dp != NULL, return -EIO);
|
||||
return snd_seq_oss_read(dp, buf, count);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
|
||||
{
|
||||
seq_oss_devinfo_t *dp;
|
||||
dp = file->private_data;
|
||||
snd_assert(dp != NULL, return -EIO);
|
||||
return snd_seq_oss_write(dp, buf, count, file);
|
||||
}
|
||||
|
||||
static long
|
||||
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
seq_oss_devinfo_t *dp;
|
||||
dp = file->private_data;
|
||||
snd_assert(dp != NULL, return -EIO);
|
||||
return snd_seq_oss_ioctl(dp, cmd, arg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define odev_ioctl_compat odev_ioctl
|
||||
#else
|
||||
#define odev_ioctl_compat NULL
|
||||
#endif
|
||||
|
||||
static unsigned int
|
||||
odev_poll(struct file *file, poll_table * wait)
|
||||
{
|
||||
seq_oss_devinfo_t *dp;
|
||||
dp = file->private_data;
|
||||
snd_assert(dp != NULL, return 0);
|
||||
return snd_seq_oss_poll(dp, file, wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* registration of sequencer minor device
|
||||
*/
|
||||
|
||||
static struct file_operations seq_oss_f_ops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.read = odev_read,
|
||||
.write = odev_write,
|
||||
.open = odev_open,
|
||||
.release = odev_release,
|
||||
.poll = odev_poll,
|
||||
.unlocked_ioctl = odev_ioctl,
|
||||
.compat_ioctl = odev_ioctl_compat,
|
||||
};
|
||||
|
||||
static snd_minor_t seq_oss_reg = {
|
||||
.comment = "sequencer",
|
||||
.f_ops = &seq_oss_f_ops,
|
||||
};
|
||||
|
||||
static int __init
|
||||
register_device(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
down(®ister_mutex);
|
||||
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
|
||||
NULL, 0,
|
||||
&seq_oss_reg,
|
||||
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
|
||||
snd_printk(KERN_ERR "can't register device seq\n");
|
||||
up(®ister_mutex);
|
||||
return rc;
|
||||
}
|
||||
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
|
||||
NULL, 0,
|
||||
&seq_oss_reg,
|
||||
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
|
||||
snd_printk(KERN_ERR "can't register device music\n");
|
||||
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
|
||||
up(®ister_mutex);
|
||||
return rc;
|
||||
}
|
||||
debug_printk(("device registered\n"));
|
||||
up(®ister_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_device(void)
|
||||
{
|
||||
down(®ister_mutex);
|
||||
debug_printk(("device unregistered\n"));
|
||||
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
|
||||
snd_printk(KERN_ERR "error unregister device music\n");
|
||||
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
|
||||
snd_printk(KERN_ERR "error unregister device seq\n");
|
||||
up(®ister_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* /proc interface
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static snd_info_entry_t *info_entry;
|
||||
|
||||
static void
|
||||
info_read(snd_info_entry_t *entry, snd_info_buffer_t *buf)
|
||||
{
|
||||
down(®ister_mutex);
|
||||
snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
|
||||
snd_seq_oss_system_info_read(buf);
|
||||
snd_seq_oss_synth_info_read(buf);
|
||||
snd_seq_oss_midi_info_read(buf);
|
||||
up(®ister_mutex);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
static int __init
|
||||
register_proc(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
|
||||
if (entry == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = NULL;
|
||||
entry->c.text.read_size = 1024;
|
||||
entry->c.text.read = info_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
info_entry = entry;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_proc(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
if (info_entry)
|
||||
snd_info_unregister(info_entry);
|
||||
info_entry = NULL;
|
||||
#endif
|
||||
}
|
||||
198
sound/core/seq/oss/seq_oss_device.h
Normal file
198
sound/core/seq/oss/seq_oss_device.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_DEVICE_H
|
||||
#define __SEQ_OSS_DEVICE_H
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/seq_oss.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
/* enable debug print */
|
||||
#define SNDRV_SEQ_OSS_DEBUG
|
||||
|
||||
/* max. applications */
|
||||
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
|
||||
#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16
|
||||
#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32
|
||||
|
||||
/* version */
|
||||
#define SNDRV_SEQ_OSS_MAJOR_VERSION 0
|
||||
#define SNDRV_SEQ_OSS_MINOR_VERSION 1
|
||||
#define SNDRV_SEQ_OSS_TINY_VERSION 8
|
||||
#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8"
|
||||
|
||||
/* device and proc interface name */
|
||||
#define SNDRV_SEQ_OSS_DEVNAME "seq_oss"
|
||||
#define SNDRV_SEQ_OSS_PROCNAME "oss"
|
||||
|
||||
|
||||
/*
|
||||
* type definitions
|
||||
*/
|
||||
|
||||
typedef struct seq_oss_devinfo_t seq_oss_devinfo_t;
|
||||
typedef struct seq_oss_writeq_t seq_oss_writeq_t;
|
||||
typedef struct seq_oss_readq_t seq_oss_readq_t;
|
||||
typedef struct seq_oss_timer_t seq_oss_timer_t;
|
||||
typedef struct seq_oss_synthinfo_t seq_oss_synthinfo_t;
|
||||
typedef struct seq_oss_synth_sysex_t seq_oss_synth_sysex_t;
|
||||
typedef struct seq_oss_chinfo_t seq_oss_chinfo_t;
|
||||
typedef unsigned int reltime_t;
|
||||
typedef unsigned int abstime_t;
|
||||
typedef union evrec_t evrec_t;
|
||||
|
||||
|
||||
/*
|
||||
* synthesizer channel information
|
||||
*/
|
||||
struct seq_oss_chinfo_t {
|
||||
int note, vel;
|
||||
};
|
||||
|
||||
/*
|
||||
* synthesizer information
|
||||
*/
|
||||
struct seq_oss_synthinfo_t {
|
||||
snd_seq_oss_arg_t arg;
|
||||
seq_oss_chinfo_t *ch;
|
||||
seq_oss_synth_sysex_t *sysex;
|
||||
int nr_voices;
|
||||
int opened;
|
||||
int is_midi;
|
||||
int midi_mapped;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* sequencer client information
|
||||
*/
|
||||
|
||||
struct seq_oss_devinfo_t {
|
||||
|
||||
int index; /* application index */
|
||||
int cseq; /* sequencer client number */
|
||||
int port; /* sequencer port number */
|
||||
int queue; /* sequencer queue number */
|
||||
|
||||
snd_seq_addr_t addr; /* address of this device */
|
||||
|
||||
int seq_mode; /* sequencer mode */
|
||||
int file_mode; /* file access */
|
||||
|
||||
/* midi device table */
|
||||
int max_mididev;
|
||||
|
||||
/* synth device table */
|
||||
int max_synthdev;
|
||||
seq_oss_synthinfo_t synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
|
||||
int synth_opened;
|
||||
|
||||
/* output queue */
|
||||
seq_oss_writeq_t *writeq;
|
||||
|
||||
/* midi input queue */
|
||||
seq_oss_readq_t *readq;
|
||||
|
||||
/* timer */
|
||||
seq_oss_timer_t *timer;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* function prototypes
|
||||
*/
|
||||
|
||||
/* create/delete OSS sequencer client */
|
||||
int snd_seq_oss_create_client(void);
|
||||
int snd_seq_oss_delete_client(void);
|
||||
|
||||
/* device file interface */
|
||||
int snd_seq_oss_open(struct file *file, int level);
|
||||
void snd_seq_oss_release(seq_oss_devinfo_t *dp);
|
||||
int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long arg);
|
||||
int snd_seq_oss_read(seq_oss_devinfo_t *dev, char __user *buf, int count);
|
||||
int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt);
|
||||
unsigned int snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait);
|
||||
|
||||
void snd_seq_oss_reset(seq_oss_devinfo_t *dp);
|
||||
void snd_seq_oss_drain_write(seq_oss_devinfo_t *dp);
|
||||
|
||||
/* */
|
||||
void snd_seq_oss_process_queue(seq_oss_devinfo_t *dp, abstime_t time);
|
||||
|
||||
|
||||
/* proc interface */
|
||||
void snd_seq_oss_system_info_read(snd_info_buffer_t *buf);
|
||||
void snd_seq_oss_midi_info_read(snd_info_buffer_t *buf);
|
||||
void snd_seq_oss_synth_info_read(snd_info_buffer_t *buf);
|
||||
void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf);
|
||||
|
||||
/* file mode macros */
|
||||
#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ)
|
||||
#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE)
|
||||
#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK)
|
||||
|
||||
/* dispatch event */
|
||||
inline static int
|
||||
snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop)
|
||||
{
|
||||
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
|
||||
}
|
||||
|
||||
/* ioctl */
|
||||
inline static int
|
||||
snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg)
|
||||
{
|
||||
return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
|
||||
}
|
||||
|
||||
/* fill the addresses in header */
|
||||
inline static void
|
||||
snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev,
|
||||
int dest_client, int dest_port)
|
||||
{
|
||||
ev->queue = dp->queue;
|
||||
ev->source = dp->addr;
|
||||
ev->dest.client = dest_client;
|
||||
ev->dest.port = dest_port;
|
||||
}
|
||||
|
||||
|
||||
/* misc. functions for proc interface */
|
||||
char *enabled_str(int bool);
|
||||
|
||||
|
||||
/* for debug */
|
||||
#ifdef SNDRV_SEQ_OSS_DEBUG
|
||||
extern int seq_oss_debug;
|
||||
#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printk x; } while (0)
|
||||
#else
|
||||
#define debug_printk(x) /**/
|
||||
#endif
|
||||
|
||||
#endif /* __SEQ_OSS_DEVICE_H */
|
||||
447
sound/core/seq/oss/seq_oss_event.c
Normal file
447
sound/core/seq/oss/seq_oss_event.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include "seq_oss_synth.h"
|
||||
#include "seq_oss_midi.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_writeq.h"
|
||||
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
|
||||
static int chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
|
||||
static int chn_common_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
|
||||
static int timing_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
|
||||
static int local_event(seq_oss_devinfo_t *dp, evrec_t *event_rec, snd_seq_event_t *ev);
|
||||
static int old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
|
||||
static int note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
|
||||
static int note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev);
|
||||
static int set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev);
|
||||
static int set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev);
|
||||
static int set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev);
|
||||
|
||||
|
||||
/*
|
||||
* convert an OSS event to ALSA event
|
||||
* return 0 : enqueued
|
||||
* non-zero : invalid - ignored
|
||||
*/
|
||||
|
||||
int
|
||||
snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
switch (q->s.code) {
|
||||
case SEQ_EXTENDED:
|
||||
return extended_event(dp, q, ev);
|
||||
|
||||
case EV_CHN_VOICE:
|
||||
return chn_voice_event(dp, q, ev);
|
||||
|
||||
case EV_CHN_COMMON:
|
||||
return chn_common_event(dp, q, ev);
|
||||
|
||||
case EV_TIMING:
|
||||
return timing_event(dp, q, ev);
|
||||
|
||||
case EV_SEQ_LOCAL:
|
||||
return local_event(dp, q, ev);
|
||||
|
||||
case EV_SYSEX:
|
||||
return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
|
||||
|
||||
case SEQ_MIDIPUTC:
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
return -EINVAL;
|
||||
/* put a midi byte */
|
||||
if (! is_write_mode(dp->file_mode))
|
||||
break;
|
||||
if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
|
||||
break;
|
||||
if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
|
||||
return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
|
||||
break;
|
||||
|
||||
case SEQ_ECHO:
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
return -EINVAL;
|
||||
return set_echo_event(dp, q, ev);
|
||||
|
||||
case SEQ_PRIVATE:
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
return -EINVAL;
|
||||
return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
|
||||
|
||||
default:
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
return -EINVAL;
|
||||
return old_event(dp, q, ev);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* old type events: mode1 only */
|
||||
static int
|
||||
old_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
switch (q->s.code) {
|
||||
case SEQ_NOTEOFF:
|
||||
return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
|
||||
|
||||
case SEQ_NOTEON:
|
||||
return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
|
||||
|
||||
case SEQ_WAIT:
|
||||
/* skip */
|
||||
break;
|
||||
|
||||
case SEQ_PGMCHANGE:
|
||||
return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
|
||||
q->n.chn, 0, q->n.note, ev);
|
||||
|
||||
case SEQ_SYNCTIMER:
|
||||
return snd_seq_oss_timer_reset(dp->timer);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 8bytes extended event: mode1 only */
|
||||
static int
|
||||
extended_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
int val;
|
||||
|
||||
switch (q->e.cmd) {
|
||||
case SEQ_NOTEOFF:
|
||||
return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
|
||||
|
||||
case SEQ_NOTEON:
|
||||
return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
|
||||
|
||||
case SEQ_PGMCHANGE:
|
||||
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
|
||||
q->e.chn, 0, q->e.p1, ev);
|
||||
|
||||
case SEQ_AFTERTOUCH:
|
||||
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
|
||||
q->e.chn, 0, q->e.p1, ev);
|
||||
|
||||
case SEQ_BALANCE:
|
||||
/* convert -128:127 to 0:127 */
|
||||
val = (char)q->e.p1;
|
||||
val = (val + 128) / 2;
|
||||
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
|
||||
q->e.chn, CTL_PAN, val, ev);
|
||||
|
||||
case SEQ_CONTROLLER:
|
||||
val = ((short)q->e.p3 << 8) | (short)q->e.p2;
|
||||
switch (q->e.p1) {
|
||||
case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
|
||||
/* -0x2000:0x1fff */
|
||||
return set_control_event(dp, q->e.dev,
|
||||
SNDRV_SEQ_EVENT_PITCHBEND,
|
||||
q->e.chn, 0, val, ev);
|
||||
case CTRL_PITCH_BENDER_RANGE:
|
||||
/* conversion: 100/semitone -> 128/semitone */
|
||||
return set_control_event(dp, q->e.dev,
|
||||
SNDRV_SEQ_EVENT_REGPARAM,
|
||||
q->e.chn, 0, val*128/100, ev);
|
||||
default:
|
||||
return set_control_event(dp, q->e.dev,
|
||||
SNDRV_SEQ_EVENT_CONTROL14,
|
||||
q->e.chn, q->e.p1, val, ev);
|
||||
}
|
||||
|
||||
case SEQ_VOLMODE:
|
||||
return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
|
||||
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* channel voice events: mode1 and 2 */
|
||||
static int
|
||||
chn_voice_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
if (q->v.chn >= 32)
|
||||
return -EINVAL;
|
||||
switch (q->v.cmd) {
|
||||
case MIDI_NOTEON:
|
||||
return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
|
||||
|
||||
case MIDI_NOTEOFF:
|
||||
return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
|
||||
|
||||
case MIDI_KEY_PRESSURE:
|
||||
return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
|
||||
q->v.chn, q->v.note, q->v.parm, ev);
|
||||
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* channel common events: mode1 and 2 */
|
||||
static int
|
||||
chn_common_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
if (q->l.chn >= 32)
|
||||
return -EINVAL;
|
||||
switch (q->l.cmd) {
|
||||
case MIDI_PGM_CHANGE:
|
||||
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
|
||||
q->l.chn, 0, q->l.p1, ev);
|
||||
|
||||
case MIDI_CTL_CHANGE:
|
||||
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
|
||||
q->l.chn, q->l.p1, q->l.val, ev);
|
||||
|
||||
case MIDI_PITCH_BEND:
|
||||
/* conversion: 0:0x3fff -> -0x2000:0x1fff */
|
||||
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
|
||||
q->l.chn, 0, q->l.val - 8192, ev);
|
||||
|
||||
case MIDI_CHN_PRESSURE:
|
||||
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
|
||||
q->l.chn, 0, q->l.val, ev);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* timer events: mode1 and mode2 */
|
||||
static int
|
||||
timing_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
switch (q->t.cmd) {
|
||||
case TMR_ECHO:
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
return set_echo_event(dp, q, ev);
|
||||
else {
|
||||
evrec_t tmp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
/* XXX: only for little-endian! */
|
||||
tmp.echo = (q->t.time << 8) | SEQ_ECHO;
|
||||
return set_echo_event(dp, &tmp, ev);
|
||||
}
|
||||
|
||||
case TMR_STOP:
|
||||
if (dp->seq_mode)
|
||||
return snd_seq_oss_timer_stop(dp->timer);
|
||||
return 0;
|
||||
|
||||
case TMR_CONTINUE:
|
||||
if (dp->seq_mode)
|
||||
return snd_seq_oss_timer_continue(dp->timer);
|
||||
return 0;
|
||||
|
||||
case TMR_TEMPO:
|
||||
if (dp->seq_mode)
|
||||
return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* local events: mode1 and 2 */
|
||||
static int
|
||||
local_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* process note-on event for OSS synth
|
||||
* three different modes are available:
|
||||
* - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode)
|
||||
* Accept note 255 as volume change.
|
||||
* - SNDRV_SEQ_OSS_PASS_EVENTS
|
||||
* Pass all events to lowlevel driver anyway
|
||||
* - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000)
|
||||
* Use key-pressure if note >= 128
|
||||
*/
|
||||
static int
|
||||
note_on_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
|
||||
{
|
||||
seq_oss_synthinfo_t *info = &dp->synths[dev];
|
||||
switch (info->arg.event_passing) {
|
||||
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
|
||||
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
|
||||
/* pass directly */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
|
||||
}
|
||||
|
||||
if (note == 255 && info->ch[ch].note >= 0) {
|
||||
/* volume control */
|
||||
int type;
|
||||
//if (! vel)
|
||||
/* set volume to zero -- note off */
|
||||
// type = SNDRV_SEQ_EVENT_NOTEOFF;
|
||||
//else
|
||||
if (info->ch[ch].vel)
|
||||
/* sample already started -- volume change */
|
||||
type = SNDRV_SEQ_EVENT_KEYPRESS;
|
||||
else
|
||||
/* sample not started -- start now */
|
||||
type = SNDRV_SEQ_EVENT_NOTEON;
|
||||
info->ch[ch].vel = vel;
|
||||
return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
|
||||
} else if (note >= 128)
|
||||
return -EINVAL; /* invalid */
|
||||
|
||||
if (note != info->ch[ch].note && info->ch[ch].note >= 0)
|
||||
/* note changed - note off at beginning */
|
||||
set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
|
||||
/* set current status */
|
||||
info->ch[ch].note = note;
|
||||
info->ch[ch].vel = vel;
|
||||
if (vel) /* non-zero velocity - start the note now */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
|
||||
return -EINVAL;
|
||||
|
||||
case SNDRV_SEQ_OSS_PASS_EVENTS:
|
||||
/* pass the event anyway */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
|
||||
|
||||
case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
|
||||
if (note >= 128) /* key pressure: shifted by 128 */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
|
||||
else /* normal note-on event */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* process note-off event for OSS synth
|
||||
*/
|
||||
static int
|
||||
note_off_event(seq_oss_devinfo_t *dp, int dev, int ch, int note, int vel, snd_seq_event_t *ev)
|
||||
{
|
||||
seq_oss_synthinfo_t *info = &dp->synths[dev];
|
||||
switch (info->arg.event_passing) {
|
||||
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
|
||||
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
|
||||
/* pass directly */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
|
||||
}
|
||||
|
||||
if (info->ch[ch].note >= 0) {
|
||||
note = info->ch[ch].note;
|
||||
info->ch[ch].vel = 0;
|
||||
info->ch[ch].note = -1;
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
|
||||
}
|
||||
return -EINVAL; /* invalid */
|
||||
|
||||
case SNDRV_SEQ_OSS_PASS_EVENTS:
|
||||
case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
|
||||
/* pass the event anyway */
|
||||
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
|
||||
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a note event
|
||||
*/
|
||||
static int
|
||||
set_note_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int note, int vel, snd_seq_event_t *ev)
|
||||
{
|
||||
if (! snd_seq_oss_synth_is_valid(dp, dev))
|
||||
return -ENXIO;
|
||||
|
||||
ev->type = type;
|
||||
snd_seq_oss_synth_addr(dp, dev, ev);
|
||||
ev->data.note.channel = ch;
|
||||
ev->data.note.note = note;
|
||||
ev->data.note.velocity = vel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a control event
|
||||
*/
|
||||
static int
|
||||
set_control_event(seq_oss_devinfo_t *dp, int dev, int type, int ch, int param, int val, snd_seq_event_t *ev)
|
||||
{
|
||||
if (! snd_seq_oss_synth_is_valid(dp, dev))
|
||||
return -ENXIO;
|
||||
|
||||
ev->type = type;
|
||||
snd_seq_oss_synth_addr(dp, dev, ev);
|
||||
ev->data.control.channel = ch;
|
||||
ev->data.control.param = param;
|
||||
ev->data.control.value = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create an echo event
|
||||
*/
|
||||
static int
|
||||
set_echo_event(seq_oss_devinfo_t *dp, evrec_t *rec, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->type = SNDRV_SEQ_EVENT_ECHO;
|
||||
/* echo back to itself */
|
||||
snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
|
||||
memcpy(&ev->data, rec, LONG_EVENT_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event input callback from ALSA sequencer:
|
||||
* the echo event is processed here.
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data,
|
||||
int atomic, int hop)
|
||||
{
|
||||
seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
|
||||
evrec_t *rec;
|
||||
|
||||
if (ev->type != SNDRV_SEQ_EVENT_ECHO)
|
||||
return snd_seq_oss_midi_input(ev, direct, private_data);
|
||||
|
||||
if (ev->source.client != dp->cseq)
|
||||
return 0; /* ignored */
|
||||
|
||||
rec = (evrec_t*)&ev->data;
|
||||
if (rec->s.code == SEQ_SYNCTIMER) {
|
||||
/* sync echo back */
|
||||
snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
|
||||
|
||||
} else {
|
||||
/* echo back event */
|
||||
if (dp->readq == NULL)
|
||||
return 0;
|
||||
snd_seq_oss_readq_put_event(dp->readq, rec);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
112
sound/core/seq/oss/seq_oss_event.h
Normal file
112
sound/core/seq/oss/seq_oss_event.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* seq_oss_event.h - OSS event queue record
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_EVENT_H
|
||||
#define __SEQ_OSS_EVENT_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
|
||||
#define SHORT_EVENT_SIZE 4
|
||||
#define LONG_EVENT_SIZE 8
|
||||
|
||||
/* short event (4bytes) */
|
||||
typedef struct evrec_short_t {
|
||||
unsigned char code;
|
||||
unsigned char parm1;
|
||||
unsigned char dev;
|
||||
unsigned char parm2;
|
||||
} evrec_short_t;
|
||||
|
||||
/* short note events (4bytes) */
|
||||
typedef struct evrec_note_t {
|
||||
unsigned char code;
|
||||
unsigned char chn;
|
||||
unsigned char note;
|
||||
unsigned char vel;
|
||||
} evrec_note_t;
|
||||
|
||||
/* long timer events (8bytes) */
|
||||
typedef struct evrec_timer_t {
|
||||
unsigned char code;
|
||||
unsigned char cmd;
|
||||
unsigned char dummy1, dummy2;
|
||||
unsigned int time;
|
||||
} evrec_timer_t;
|
||||
|
||||
/* long extended events (8bytes) */
|
||||
typedef struct evrec_extended_t {
|
||||
unsigned char code;
|
||||
unsigned char cmd;
|
||||
unsigned char dev;
|
||||
unsigned char chn;
|
||||
unsigned char p1, p2, p3, p4;
|
||||
} evrec_extended_t;
|
||||
|
||||
/* long channel events (8bytes) */
|
||||
typedef struct evrec_long_t {
|
||||
unsigned char code;
|
||||
unsigned char dev;
|
||||
unsigned char cmd;
|
||||
unsigned char chn;
|
||||
unsigned char p1, p2;
|
||||
unsigned short val;
|
||||
} evrec_long_t;
|
||||
|
||||
/* channel voice events (8bytes) */
|
||||
typedef struct evrec_voice_t {
|
||||
unsigned char code;
|
||||
unsigned char dev;
|
||||
unsigned char cmd;
|
||||
unsigned char chn;
|
||||
unsigned char note, parm;
|
||||
unsigned short dummy;
|
||||
} evrec_voice_t;
|
||||
|
||||
/* sysex events (8bytes) */
|
||||
typedef struct evrec_sysex_t {
|
||||
unsigned char code;
|
||||
unsigned char dev;
|
||||
unsigned char buf[6];
|
||||
} evrec_sysex_t;
|
||||
|
||||
/* event record */
|
||||
union evrec_t {
|
||||
evrec_short_t s;
|
||||
evrec_note_t n;
|
||||
evrec_long_t l;
|
||||
evrec_voice_t v;
|
||||
evrec_timer_t t;
|
||||
evrec_extended_t e;
|
||||
evrec_sysex_t x;
|
||||
unsigned int echo;
|
||||
unsigned char c[LONG_EVENT_SIZE];
|
||||
};
|
||||
|
||||
#define ev_is_long(ev) ((ev)->s.code >= 128)
|
||||
#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
|
||||
|
||||
int snd_seq_oss_process_event(seq_oss_devinfo_t *dp, evrec_t *q, snd_seq_event_t *ev);
|
||||
int snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *q);
|
||||
int snd_seq_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop);
|
||||
|
||||
|
||||
#endif /* __SEQ_OSS_EVENT_H */
|
||||
555
sound/core/seq/oss/seq_oss_init.c
Normal file
555
sound/core/seq/oss/seq_oss_init.c
Normal file
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* open/close and reset interface
|
||||
*
|
||||
* Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include "seq_oss_synth.h"
|
||||
#include "seq_oss_midi.h"
|
||||
#include "seq_oss_writeq.h"
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
/*
|
||||
* common variables
|
||||
*/
|
||||
static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
|
||||
module_param(maxqlen, int, 0444);
|
||||
MODULE_PARM_DESC(maxqlen, "maximum queue length");
|
||||
|
||||
static int system_client = -1; /* ALSA sequencer client number */
|
||||
static int system_port = -1;
|
||||
|
||||
static int num_clients;
|
||||
static seq_oss_devinfo_t *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
|
||||
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
|
||||
static int translate_mode(struct file *file);
|
||||
static int create_port(seq_oss_devinfo_t *dp);
|
||||
static int delete_port(seq_oss_devinfo_t *dp);
|
||||
static int alloc_seq_queue(seq_oss_devinfo_t *dp);
|
||||
static int delete_seq_queue(int queue);
|
||||
static void free_devinfo(void *private);
|
||||
|
||||
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
|
||||
|
||||
|
||||
/*
|
||||
* create sequencer client for OSS sequencer
|
||||
*/
|
||||
int __init
|
||||
snd_seq_oss_create_client(void)
|
||||
{
|
||||
int rc;
|
||||
snd_seq_client_callback_t callback;
|
||||
snd_seq_client_info_t *info;
|
||||
snd_seq_port_info_t *port;
|
||||
snd_seq_port_callback_t port_callback;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
port = kmalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!info || !port) {
|
||||
rc = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
/* create ALSA client */
|
||||
memset(&callback, 0, sizeof(callback));
|
||||
|
||||
callback.private_data = NULL;
|
||||
callback.allow_input = 1;
|
||||
callback.allow_output = 1;
|
||||
|
||||
rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS, &callback);
|
||||
if (rc < 0)
|
||||
goto __error;
|
||||
|
||||
system_client = rc;
|
||||
debug_printk(("new client = %d\n", rc));
|
||||
|
||||
/* set client information */
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->client = system_client;
|
||||
info->type = KERNEL_CLIENT;
|
||||
strcpy(info->name, "OSS sequencer");
|
||||
|
||||
rc = call_ctl(SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
|
||||
|
||||
/* look up midi devices */
|
||||
snd_seq_oss_midi_lookup_ports(system_client);
|
||||
|
||||
/* create annoucement receiver port */
|
||||
memset(port, 0, sizeof(*port));
|
||||
strcpy(port->name, "Receiver");
|
||||
port->addr.client = system_client;
|
||||
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
|
||||
port->type = 0;
|
||||
|
||||
memset(&port_callback, 0, sizeof(port_callback));
|
||||
/* don't set port_callback.owner here. otherwise the module counter
|
||||
* is incremented and we can no longer release the module..
|
||||
*/
|
||||
port_callback.event_input = receive_announce;
|
||||
port->kernel = &port_callback;
|
||||
|
||||
call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
|
||||
if ((system_port = port->addr.port) >= 0) {
|
||||
snd_seq_port_subscribe_t subs;
|
||||
|
||||
memset(&subs, 0, sizeof(subs));
|
||||
subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
|
||||
subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
|
||||
subs.dest.client = system_client;
|
||||
subs.dest.port = system_port;
|
||||
call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
__error:
|
||||
kfree(port);
|
||||
kfree(info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* receive annoucement from system port, and check the midi device
|
||||
*/
|
||||
static int
|
||||
receive_announce(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop)
|
||||
{
|
||||
snd_seq_port_info_t pinfo;
|
||||
|
||||
if (atomic)
|
||||
return 0; /* it must not happen */
|
||||
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_PORT_START:
|
||||
case SNDRV_SEQ_EVENT_PORT_CHANGE:
|
||||
if (ev->data.addr.client == system_client)
|
||||
break; /* ignore myself */
|
||||
memset(&pinfo, 0, sizeof(pinfo));
|
||||
pinfo.addr = ev->data.addr;
|
||||
if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
|
||||
snd_seq_oss_midi_check_new_port(&pinfo);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_PORT_EXIT:
|
||||
if (ev->data.addr.client == system_client)
|
||||
break; /* ignore myself */
|
||||
snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
|
||||
ev->data.addr.port);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* delete OSS sequencer client
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_delete_client(void)
|
||||
{
|
||||
if (system_client >= 0)
|
||||
snd_seq_delete_kernel_client(system_client);
|
||||
|
||||
snd_seq_oss_midi_clear_all();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open sequencer device
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_open(struct file *file, int level)
|
||||
{
|
||||
int i, rc;
|
||||
seq_oss_devinfo_t *dp;
|
||||
|
||||
if ((dp = kcalloc(1, sizeof(*dp), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't malloc device info\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
debug_printk(("oss_open: dp = %p\n", dp));
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
|
||||
if (client_table[i] == NULL)
|
||||
break;
|
||||
}
|
||||
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
|
||||
snd_printk(KERN_ERR "too many applications\n");
|
||||
kfree(dp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dp->index = i;
|
||||
dp->cseq = system_client;
|
||||
dp->port = -1;
|
||||
dp->queue = -1;
|
||||
dp->readq = NULL;
|
||||
dp->writeq = NULL;
|
||||
|
||||
/* look up synth and midi devices */
|
||||
snd_seq_oss_synth_setup(dp);
|
||||
snd_seq_oss_midi_setup(dp);
|
||||
|
||||
if (dp->synth_opened == 0 && dp->max_mididev == 0) {
|
||||
/* snd_printk(KERN_ERR "no device found\n"); */
|
||||
rc = -ENODEV;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
/* create port */
|
||||
debug_printk(("create new port\n"));
|
||||
if ((rc = create_port(dp)) < 0) {
|
||||
snd_printk(KERN_ERR "can't create port\n");
|
||||
goto _error;
|
||||
}
|
||||
|
||||
/* allocate queue */
|
||||
debug_printk(("allocate queue\n"));
|
||||
if ((rc = alloc_seq_queue(dp)) < 0)
|
||||
goto _error;
|
||||
|
||||
/* set address */
|
||||
dp->addr.client = dp->cseq;
|
||||
dp->addr.port = dp->port;
|
||||
/*dp->addr.queue = dp->queue;*/
|
||||
/*dp->addr.channel = 0;*/
|
||||
|
||||
dp->seq_mode = level;
|
||||
|
||||
/* set up file mode */
|
||||
dp->file_mode = translate_mode(file);
|
||||
|
||||
/* initialize read queue */
|
||||
debug_printk(("initialize read queue\n"));
|
||||
if (is_read_mode(dp->file_mode)) {
|
||||
if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto _error;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize write queue */
|
||||
debug_printk(("initialize write queue\n"));
|
||||
if (is_write_mode(dp->file_mode)) {
|
||||
dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
|
||||
if (dp->writeq == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto _error;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize timer */
|
||||
debug_printk(("initialize timer\n"));
|
||||
if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't alloc timer\n");
|
||||
rc = -ENOMEM;
|
||||
goto _error;
|
||||
}
|
||||
debug_printk(("timer initialized\n"));
|
||||
|
||||
/* set private data pointer */
|
||||
file->private_data = dp;
|
||||
|
||||
/* set up for mode2 */
|
||||
if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
snd_seq_oss_synth_setup_midi(dp);
|
||||
else if (is_read_mode(dp->file_mode))
|
||||
snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
|
||||
|
||||
client_table[dp->index] = dp;
|
||||
num_clients++;
|
||||
|
||||
debug_printk(("open done\n"));
|
||||
return 0;
|
||||
|
||||
_error:
|
||||
snd_seq_oss_synth_cleanup(dp);
|
||||
snd_seq_oss_midi_cleanup(dp);
|
||||
i = dp->queue;
|
||||
delete_port(dp);
|
||||
delete_seq_queue(i);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* translate file flags to private mode
|
||||
*/
|
||||
static int
|
||||
translate_mode(struct file *file)
|
||||
{
|
||||
int file_mode = 0;
|
||||
if ((file->f_flags & O_ACCMODE) != O_RDONLY)
|
||||
file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
|
||||
if ((file->f_flags & O_ACCMODE) != O_WRONLY)
|
||||
file_mode |= SNDRV_SEQ_OSS_FILE_READ;
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
|
||||
return file_mode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create sequencer port
|
||||
*/
|
||||
static int
|
||||
create_port(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int rc;
|
||||
snd_seq_port_info_t port;
|
||||
snd_seq_port_callback_t callback;
|
||||
|
||||
memset(&port, 0, sizeof(port));
|
||||
port.addr.client = dp->cseq;
|
||||
sprintf(port.name, "Sequencer-%d", dp->index);
|
||||
port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
|
||||
port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
|
||||
port.midi_channels = 128;
|
||||
port.synth_voices = 128;
|
||||
|
||||
memset(&callback, 0, sizeof(callback));
|
||||
callback.owner = THIS_MODULE;
|
||||
callback.private_data = dp;
|
||||
callback.event_input = snd_seq_oss_event_input;
|
||||
callback.private_free = free_devinfo;
|
||||
port.kernel = &callback;
|
||||
|
||||
rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
dp->port = port.addr.port;
|
||||
debug_printk(("new port = %d\n", port.addr.port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete ALSA port
|
||||
*/
|
||||
static int
|
||||
delete_port(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
if (dp->port < 0)
|
||||
return 0;
|
||||
|
||||
debug_printk(("delete_port %i\n", dp->port));
|
||||
return snd_seq_event_port_detach(dp->cseq, dp->port);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a queue
|
||||
*/
|
||||
static int
|
||||
alloc_seq_queue(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
snd_seq_queue_info_t qinfo;
|
||||
int rc;
|
||||
|
||||
memset(&qinfo, 0, sizeof(qinfo));
|
||||
qinfo.owner = system_client;
|
||||
qinfo.locked = 1;
|
||||
strcpy(qinfo.name, "OSS Sequencer Emulation");
|
||||
if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
|
||||
return rc;
|
||||
dp->queue = qinfo.queue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release queue
|
||||
*/
|
||||
static int
|
||||
delete_seq_queue(int queue)
|
||||
{
|
||||
snd_seq_queue_info_t qinfo;
|
||||
int rc;
|
||||
|
||||
if (queue < 0)
|
||||
return 0;
|
||||
memset(&qinfo, 0, sizeof(qinfo));
|
||||
qinfo.queue = queue;
|
||||
rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
|
||||
if (rc < 0)
|
||||
printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* free device informations - private_free callback of port
|
||||
*/
|
||||
static void
|
||||
free_devinfo(void *private)
|
||||
{
|
||||
seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private;
|
||||
|
||||
if (dp->timer)
|
||||
snd_seq_oss_timer_delete(dp->timer);
|
||||
|
||||
if (dp->writeq)
|
||||
snd_seq_oss_writeq_delete(dp->writeq);
|
||||
|
||||
if (dp->readq)
|
||||
snd_seq_oss_readq_delete(dp->readq);
|
||||
|
||||
kfree(dp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* close sequencer device
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_release(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int queue;
|
||||
|
||||
client_table[dp->index] = NULL;
|
||||
num_clients--;
|
||||
|
||||
debug_printk(("resetting..\n"));
|
||||
snd_seq_oss_reset(dp);
|
||||
|
||||
debug_printk(("cleaning up..\n"));
|
||||
snd_seq_oss_synth_cleanup(dp);
|
||||
snd_seq_oss_midi_cleanup(dp);
|
||||
|
||||
/* clear slot */
|
||||
debug_printk(("releasing resource..\n"));
|
||||
queue = dp->queue;
|
||||
if (dp->port >= 0)
|
||||
delete_port(dp);
|
||||
delete_seq_queue(queue);
|
||||
|
||||
debug_printk(("release done\n"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wait until the queue is empty (if we don't have nonblock)
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_drain_write(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
if (! dp->timer->running)
|
||||
return;
|
||||
if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
|
||||
dp->writeq) {
|
||||
debug_printk(("syncing..\n"));
|
||||
while (snd_seq_oss_writeq_sync(dp->writeq))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset sequencer devices
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_reset(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* reset all synth devices */
|
||||
for (i = 0; i < dp->max_synthdev; i++)
|
||||
snd_seq_oss_synth_reset(dp, i);
|
||||
|
||||
/* reset all midi devices */
|
||||
if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
|
||||
for (i = 0; i < dp->max_mididev; i++)
|
||||
snd_seq_oss_midi_reset(dp, i);
|
||||
}
|
||||
|
||||
/* remove queues */
|
||||
if (dp->readq)
|
||||
snd_seq_oss_readq_clear(dp->readq);
|
||||
if (dp->writeq)
|
||||
snd_seq_oss_writeq_clear(dp->writeq);
|
||||
|
||||
/* reset timer */
|
||||
snd_seq_oss_timer_stop(dp->timer);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* misc. functions for proc interface
|
||||
*/
|
||||
char *
|
||||
enabled_str(int bool)
|
||||
{
|
||||
return bool ? "enabled" : "disabled";
|
||||
}
|
||||
|
||||
static char *
|
||||
filemode_str(int val)
|
||||
{
|
||||
static char *str[] = {
|
||||
"none", "read", "write", "read/write",
|
||||
};
|
||||
return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* proc interface
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_system_info_read(snd_info_buffer_t *buf)
|
||||
{
|
||||
int i;
|
||||
seq_oss_devinfo_t *dp;
|
||||
|
||||
snd_iprintf(buf, "ALSA client number %d\n", system_client);
|
||||
snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
|
||||
|
||||
snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
|
||||
for (i = 0; i < num_clients; i++) {
|
||||
snd_iprintf(buf, "\nApplication %d: ", i);
|
||||
if ((dp = client_table[i]) == NULL) {
|
||||
snd_iprintf(buf, "*empty*\n");
|
||||
continue;
|
||||
}
|
||||
snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
|
||||
snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n",
|
||||
(dp->seq_mode ? "music" : "synth"),
|
||||
filemode_str(dp->file_mode));
|
||||
if (dp->seq_mode)
|
||||
snd_iprintf(buf, " timer tempo = %d, timebase = %d\n",
|
||||
dp->timer->oss_tempo, dp->timer->oss_timebase);
|
||||
snd_iprintf(buf, " max queue length %d\n", maxqlen);
|
||||
if (is_read_mode(dp->file_mode) && dp->readq)
|
||||
snd_seq_oss_readq_info_read(dp->readq, buf);
|
||||
}
|
||||
}
|
||||
|
||||
209
sound/core/seq/oss/seq_oss_ioctl.c
Normal file
209
sound/core/seq/oss/seq_oss_ioctl.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* OSS compatible i/o control
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_writeq.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include "seq_oss_synth.h"
|
||||
#include "seq_oss_midi.h"
|
||||
#include "seq_oss_event.h"
|
||||
|
||||
static int snd_seq_oss_synth_info_user(seq_oss_devinfo_t *dp, void __user *arg)
|
||||
{
|
||||
struct synth_info info;
|
||||
|
||||
if (copy_from_user(&info, arg, sizeof(info)))
|
||||
return -EFAULT;
|
||||
if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0)
|
||||
return -EINVAL;
|
||||
if (copy_to_user(arg, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_oss_midi_info_user(seq_oss_devinfo_t *dp, void __user *arg)
|
||||
{
|
||||
struct midi_info info;
|
||||
|
||||
if (copy_from_user(&info, arg, sizeof(info)))
|
||||
return -EFAULT;
|
||||
if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0)
|
||||
return -EINVAL;
|
||||
if (copy_to_user(arg, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_oss_oob_user(seq_oss_devinfo_t *dp, void __user *arg)
|
||||
{
|
||||
unsigned char ev[8];
|
||||
snd_seq_event_t tmpev;
|
||||
|
||||
if (copy_from_user(ev, arg, 8))
|
||||
return -EFAULT;
|
||||
memset(&tmpev, 0, sizeof(tmpev));
|
||||
snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
|
||||
tmpev.time.tick = 0;
|
||||
if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) {
|
||||
snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg)
|
||||
{
|
||||
int dev, val;
|
||||
void __user *arg = (void __user *)carg;
|
||||
int __user *p = arg;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDCTL_TMR_TIMEBASE:
|
||||
case SNDCTL_TMR_TEMPO:
|
||||
case SNDCTL_TMR_START:
|
||||
case SNDCTL_TMR_STOP:
|
||||
case SNDCTL_TMR_CONTINUE:
|
||||
case SNDCTL_TMR_METRONOME:
|
||||
case SNDCTL_TMR_SOURCE:
|
||||
case SNDCTL_TMR_SELECT:
|
||||
case SNDCTL_SEQ_CTRLRATE:
|
||||
return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
|
||||
|
||||
case SNDCTL_SEQ_PANIC:
|
||||
debug_printk(("panic\n"));
|
||||
snd_seq_oss_reset(dp);
|
||||
return -EINVAL;
|
||||
|
||||
case SNDCTL_SEQ_SYNC:
|
||||
debug_printk(("sync\n"));
|
||||
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
|
||||
return 0;
|
||||
while (snd_seq_oss_writeq_sync(dp->writeq))
|
||||
;
|
||||
if (signal_pending(current))
|
||||
return -ERESTARTSYS;
|
||||
return 0;
|
||||
|
||||
case SNDCTL_SEQ_RESET:
|
||||
debug_printk(("reset\n"));
|
||||
snd_seq_oss_reset(dp);
|
||||
return 0;
|
||||
|
||||
case SNDCTL_SEQ_TESTMIDI:
|
||||
debug_printk(("test midi\n"));
|
||||
if (get_user(dev, p))
|
||||
return -EFAULT;
|
||||
return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
|
||||
|
||||
case SNDCTL_SEQ_GETINCOUNT:
|
||||
debug_printk(("get in count\n"));
|
||||
if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
|
||||
return 0;
|
||||
return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_SEQ_GETOUTCOUNT:
|
||||
debug_printk(("get out count\n"));
|
||||
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
|
||||
return 0;
|
||||
return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_SEQ_GETTIME:
|
||||
debug_printk(("get time\n"));
|
||||
return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_SEQ_RESETSAMPLES:
|
||||
debug_printk(("reset samples\n"));
|
||||
if (get_user(dev, p))
|
||||
return -EFAULT;
|
||||
return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
|
||||
|
||||
case SNDCTL_SEQ_NRSYNTHS:
|
||||
debug_printk(("nr synths\n"));
|
||||
return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_SEQ_NRMIDIS:
|
||||
debug_printk(("nr midis\n"));
|
||||
return put_user(dp->max_mididev, p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_SYNTH_MEMAVL:
|
||||
debug_printk(("mem avail\n"));
|
||||
if (get_user(dev, p))
|
||||
return -EFAULT;
|
||||
val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
|
||||
return put_user(val, p) ? -EFAULT : 0;
|
||||
|
||||
case SNDCTL_FM_4OP_ENABLE:
|
||||
debug_printk(("4op\n"));
|
||||
if (get_user(dev, p))
|
||||
return -EFAULT;
|
||||
snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
|
||||
return 0;
|
||||
|
||||
case SNDCTL_SYNTH_INFO:
|
||||
case SNDCTL_SYNTH_ID:
|
||||
debug_printk(("synth info\n"));
|
||||
return snd_seq_oss_synth_info_user(dp, arg);
|
||||
|
||||
case SNDCTL_SEQ_OUTOFBAND:
|
||||
debug_printk(("out of band\n"));
|
||||
return snd_seq_oss_oob_user(dp, arg);
|
||||
|
||||
case SNDCTL_MIDI_INFO:
|
||||
debug_printk(("midi info\n"));
|
||||
return snd_seq_oss_midi_info_user(dp, arg);
|
||||
|
||||
case SNDCTL_SEQ_THRESHOLD:
|
||||
debug_printk(("threshold\n"));
|
||||
if (! is_write_mode(dp->file_mode))
|
||||
return 0;
|
||||
if (get_user(val, p))
|
||||
return -EFAULT;
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
if (val >= dp->writeq->maxlen)
|
||||
val = dp->writeq->maxlen - 1;
|
||||
snd_seq_oss_writeq_set_output(dp->writeq, val);
|
||||
return 0;
|
||||
|
||||
case SNDCTL_MIDI_PRETIME:
|
||||
debug_printk(("pretime\n"));
|
||||
if (dp->readq == NULL || !is_read_mode(dp->file_mode))
|
||||
return 0;
|
||||
if (get_user(val, p))
|
||||
return -EFAULT;
|
||||
if (val <= 0)
|
||||
val = -1;
|
||||
else
|
||||
val = (HZ * val) / 10;
|
||||
dp->readq->pre_event_timeout = val;
|
||||
return put_user(val, p) ? -EFAULT : 0;
|
||||
|
||||
default:
|
||||
debug_printk(("others\n"));
|
||||
if (! is_write_mode(dp->file_mode))
|
||||
return -EIO;
|
||||
return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
710
sound/core/seq/oss/seq_oss_midi.c
Normal file
710
sound/core/seq/oss/seq_oss_midi.c
Normal file
@@ -0,0 +1,710 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* MIDI device handlers
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_midi.h"
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include <sound/seq_midi_event.h>
|
||||
#include "../seq_lock.h"
|
||||
#include <linux/init.h>
|
||||
|
||||
|
||||
/*
|
||||
* constants
|
||||
*/
|
||||
#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30
|
||||
|
||||
/*
|
||||
* definition of midi device record
|
||||
*/
|
||||
struct seq_oss_midi_t {
|
||||
int seq_device; /* device number */
|
||||
int client; /* sequencer client number */
|
||||
int port; /* sequencer port number */
|
||||
unsigned int flags; /* port capability */
|
||||
int opened; /* flag for opening */
|
||||
unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
|
||||
snd_midi_event_t *coder; /* MIDI event coder */
|
||||
seq_oss_devinfo_t *devinfo; /* assigned OSSseq device */
|
||||
snd_use_lock_t use_lock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* midi device table
|
||||
*/
|
||||
static int max_midi_devs;
|
||||
static seq_oss_midi_t *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
|
||||
|
||||
static DEFINE_SPINLOCK(register_lock);
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static seq_oss_midi_t *get_mdev(int dev);
|
||||
static seq_oss_midi_t *get_mididev(seq_oss_devinfo_t *dp, int dev);
|
||||
static int send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev);
|
||||
static int send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev);
|
||||
|
||||
/*
|
||||
* look up the existing ports
|
||||
* this looks a very exhausting job.
|
||||
*/
|
||||
int __init
|
||||
snd_seq_oss_midi_lookup_ports(int client)
|
||||
{
|
||||
snd_seq_client_info_t *clinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
|
||||
clinfo = kcalloc(1, sizeof(*clinfo), GFP_KERNEL);
|
||||
pinfo = kcalloc(1, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (! clinfo || ! pinfo) {
|
||||
kfree(clinfo);
|
||||
kfree(pinfo);
|
||||
return -ENOMEM;
|
||||
}
|
||||
clinfo->client = -1;
|
||||
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
|
||||
if (clinfo->client == client)
|
||||
continue; /* ignore myself */
|
||||
pinfo->addr.client = clinfo->client;
|
||||
pinfo->addr.port = -1;
|
||||
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
|
||||
snd_seq_oss_midi_check_new_port(pinfo);
|
||||
}
|
||||
kfree(clinfo);
|
||||
kfree(pinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
static seq_oss_midi_t *
|
||||
get_mdev(int dev)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
mdev = midi_devs[dev];
|
||||
if (mdev)
|
||||
snd_use_lock_use(&mdev->use_lock);
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
return mdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* look for the identical slot
|
||||
*/
|
||||
static seq_oss_midi_t *
|
||||
find_slot(int client, int port)
|
||||
{
|
||||
int i;
|
||||
seq_oss_midi_t *mdev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (i = 0; i < max_midi_devs; i++) {
|
||||
mdev = midi_devs[i];
|
||||
if (mdev && mdev->client == client && mdev->port == port) {
|
||||
/* found! */
|
||||
snd_use_lock_use(&mdev->use_lock);
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
return mdev;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
|
||||
#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
|
||||
/*
|
||||
* register a new port if it doesn't exist yet
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo)
|
||||
{
|
||||
int i;
|
||||
seq_oss_midi_t *mdev;
|
||||
unsigned long flags;
|
||||
|
||||
debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
|
||||
/* the port must include generic midi */
|
||||
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
|
||||
return 0;
|
||||
/* either read or write subscribable */
|
||||
if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
|
||||
(pinfo->capability & PERM_READ) != PERM_READ)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* look for the identical slot
|
||||
*/
|
||||
if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
|
||||
/* already exists */
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate midi info record
|
||||
*/
|
||||
if ((mdev = kcalloc(1, sizeof(*mdev), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't malloc midi info\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* copy the port information */
|
||||
mdev->client = pinfo->addr.client;
|
||||
mdev->port = pinfo->addr.port;
|
||||
mdev->flags = pinfo->capability;
|
||||
mdev->opened = 0;
|
||||
snd_use_lock_init(&mdev->use_lock);
|
||||
|
||||
/* copy and truncate the name of synth device */
|
||||
strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
|
||||
|
||||
/* create MIDI coder */
|
||||
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
|
||||
snd_printk(KERN_ERR "can't malloc midi coder\n");
|
||||
kfree(mdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* OSS sequencer adds running status to all sequences */
|
||||
snd_midi_event_no_status(mdev->coder, 1);
|
||||
|
||||
/*
|
||||
* look for en empty slot
|
||||
*/
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (i = 0; i < max_midi_devs; i++) {
|
||||
if (midi_devs[i] == NULL)
|
||||
break;
|
||||
}
|
||||
if (i >= max_midi_devs) {
|
||||
if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
snd_midi_event_free(mdev->coder);
|
||||
kfree(mdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
max_midi_devs++;
|
||||
}
|
||||
mdev->seq_device = i;
|
||||
midi_devs[mdev->seq_device] = mdev;
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release the midi device if it was registered
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_check_exit_port(int client, int port)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
unsigned long flags;
|
||||
int index;
|
||||
|
||||
if ((mdev = find_slot(client, port)) != NULL) {
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
midi_devs[mdev->seq_device] = NULL;
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
snd_use_lock_sync(&mdev->use_lock);
|
||||
if (mdev->coder)
|
||||
snd_midi_event_free(mdev->coder);
|
||||
kfree(mdev);
|
||||
}
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (index = max_midi_devs - 1; index >= 0; index--) {
|
||||
if (midi_devs[index])
|
||||
break;
|
||||
}
|
||||
max_midi_devs = index + 1;
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release the midi device if it was registered
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_clear_all(void)
|
||||
{
|
||||
int i;
|
||||
seq_oss_midi_t *mdev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (i = 0; i < max_midi_devs; i++) {
|
||||
if ((mdev = midi_devs[i]) != NULL) {
|
||||
if (mdev->coder)
|
||||
snd_midi_event_free(mdev->coder);
|
||||
kfree(mdev);
|
||||
midi_devs[i] = NULL;
|
||||
}
|
||||
}
|
||||
max_midi_devs = 0;
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set up midi tables
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
dp->max_mididev = max_midi_devs;
|
||||
}
|
||||
|
||||
/*
|
||||
* clean up midi tables
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dp->max_mididev; i++)
|
||||
snd_seq_oss_midi_close(dp, i);
|
||||
dp->max_mididev = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open all midi devices. ignore errors.
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dp->max_mididev; i++)
|
||||
snd_seq_oss_midi_open(dp, i, file_mode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get the midi device information
|
||||
*/
|
||||
static seq_oss_midi_t *
|
||||
get_mididev(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
if (dev < 0 || dev >= dp->max_mididev)
|
||||
return NULL;
|
||||
return get_mdev(dev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open the midi device if not opened yet
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int fmode)
|
||||
{
|
||||
int perm;
|
||||
seq_oss_midi_t *mdev;
|
||||
snd_seq_port_subscribe_t subs;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* already used? */
|
||||
if (mdev->opened && mdev->devinfo != dp) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
perm = 0;
|
||||
if (is_write_mode(fmode))
|
||||
perm |= PERM_WRITE;
|
||||
if (is_read_mode(fmode))
|
||||
perm |= PERM_READ;
|
||||
perm &= mdev->flags;
|
||||
if (perm == 0) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* already opened? */
|
||||
if ((mdev->opened & perm) == perm) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
perm &= ~mdev->opened;
|
||||
|
||||
memset(&subs, 0, sizeof(subs));
|
||||
|
||||
if (perm & PERM_WRITE) {
|
||||
subs.sender = dp->addr;
|
||||
subs.dest.client = mdev->client;
|
||||
subs.dest.port = mdev->port;
|
||||
if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
|
||||
mdev->opened |= PERM_WRITE;
|
||||
}
|
||||
if (perm & PERM_READ) {
|
||||
subs.sender.client = mdev->client;
|
||||
subs.sender.port = mdev->port;
|
||||
subs.dest = dp->addr;
|
||||
subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
|
||||
subs.queue = dp->queue; /* queue for timestamps */
|
||||
if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
|
||||
mdev->opened |= PERM_READ;
|
||||
}
|
||||
|
||||
if (! mdev->opened) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mdev->devinfo = dp;
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close the midi device if already opened
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
snd_seq_port_subscribe_t subs;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return -ENODEV;
|
||||
if (! mdev->opened || mdev->devinfo != dp) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
|
||||
memset(&subs, 0, sizeof(subs));
|
||||
if (mdev->opened & PERM_WRITE) {
|
||||
subs.sender = dp->addr;
|
||||
subs.dest.client = mdev->client;
|
||||
subs.dest.port = mdev->port;
|
||||
snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
|
||||
}
|
||||
if (mdev->opened & PERM_READ) {
|
||||
subs.sender.client = mdev->client;
|
||||
subs.sender.port = mdev->port;
|
||||
subs.dest = dp->addr;
|
||||
snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
|
||||
}
|
||||
|
||||
mdev->opened = 0;
|
||||
mdev->devinfo = NULL;
|
||||
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* change seq capability flags to file mode flags
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
int mode;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return 0;
|
||||
|
||||
mode = 0;
|
||||
if (mdev->opened & PERM_WRITE)
|
||||
mode |= SNDRV_SEQ_OSS_FILE_WRITE;
|
||||
if (mdev->opened & PERM_READ)
|
||||
mode |= SNDRV_SEQ_OSS_FILE_READ;
|
||||
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/*
|
||||
* reset the midi device and close it:
|
||||
* so far, only close the device.
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return;
|
||||
if (! mdev->opened) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mdev->opened & PERM_WRITE) {
|
||||
snd_seq_event_t ev;
|
||||
int c;
|
||||
|
||||
debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.dest.client = mdev->client;
|
||||
ev.dest.port = mdev->port;
|
||||
ev.queue = dp->queue;
|
||||
ev.source.port = dp->port;
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
|
||||
ev.type = SNDRV_SEQ_EVENT_SENSING;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
|
||||
}
|
||||
for (c = 0; c < 16; c++) {
|
||||
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
|
||||
ev.data.control.channel = c;
|
||||
ev.data.control.param = 123;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
|
||||
ev.data.control.param = 121;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
|
||||
ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
|
||||
ev.data.control.value = 0;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
|
||||
}
|
||||
}
|
||||
}
|
||||
// snd_seq_oss_midi_close(dp, dev);
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get client/port of the specified MIDI device
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return;
|
||||
addr->client = mdev->client;
|
||||
addr->port = mdev->port;
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* input callback - this can be atomic
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private_data)
|
||||
{
|
||||
seq_oss_devinfo_t *dp = (seq_oss_devinfo_t *)private_data;
|
||||
seq_oss_midi_t *mdev;
|
||||
int rc;
|
||||
|
||||
if (dp->readq == NULL)
|
||||
return 0;
|
||||
if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
|
||||
return 0;
|
||||
if (! (mdev->opened & PERM_READ)) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
|
||||
rc = send_synth_event(dp, ev, mdev->seq_device);
|
||||
else
|
||||
rc = send_midi_event(dp, ev, mdev);
|
||||
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert ALSA sequencer event to OSS synth event
|
||||
*/
|
||||
static int
|
||||
send_synth_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dev)
|
||||
{
|
||||
evrec_t ossev;
|
||||
|
||||
memset(&ossev, 0, sizeof(ossev));
|
||||
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_NOTEON:
|
||||
ossev.v.cmd = MIDI_NOTEON; break;
|
||||
case SNDRV_SEQ_EVENT_NOTEOFF:
|
||||
ossev.v.cmd = MIDI_NOTEOFF; break;
|
||||
case SNDRV_SEQ_EVENT_KEYPRESS:
|
||||
ossev.v.cmd = MIDI_KEY_PRESSURE; break;
|
||||
case SNDRV_SEQ_EVENT_CONTROLLER:
|
||||
ossev.l.cmd = MIDI_CTL_CHANGE; break;
|
||||
case SNDRV_SEQ_EVENT_PGMCHANGE:
|
||||
ossev.l.cmd = MIDI_PGM_CHANGE; break;
|
||||
case SNDRV_SEQ_EVENT_CHANPRESS:
|
||||
ossev.l.cmd = MIDI_CHN_PRESSURE; break;
|
||||
case SNDRV_SEQ_EVENT_PITCHBEND:
|
||||
ossev.l.cmd = MIDI_PITCH_BEND; break;
|
||||
default:
|
||||
return 0; /* not supported */
|
||||
}
|
||||
|
||||
ossev.v.dev = dev;
|
||||
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_NOTEON:
|
||||
case SNDRV_SEQ_EVENT_NOTEOFF:
|
||||
case SNDRV_SEQ_EVENT_KEYPRESS:
|
||||
ossev.v.code = EV_CHN_VOICE;
|
||||
ossev.v.note = ev->data.note.note;
|
||||
ossev.v.parm = ev->data.note.velocity;
|
||||
ossev.v.chn = ev->data.note.channel;
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_CONTROLLER:
|
||||
case SNDRV_SEQ_EVENT_PGMCHANGE:
|
||||
case SNDRV_SEQ_EVENT_CHANPRESS:
|
||||
ossev.l.code = EV_CHN_COMMON;
|
||||
ossev.l.p1 = ev->data.control.param;
|
||||
ossev.l.val = ev->data.control.value;
|
||||
ossev.l.chn = ev->data.control.channel;
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_PITCHBEND:
|
||||
ossev.l.code = EV_CHN_COMMON;
|
||||
ossev.l.val = ev->data.control.value + 8192;
|
||||
ossev.l.chn = ev->data.control.channel;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
|
||||
snd_seq_oss_readq_put_event(dp->readq, &ossev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* decode event and send MIDI bytes to read queue
|
||||
*/
|
||||
static int
|
||||
send_midi_event(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, seq_oss_midi_t *mdev)
|
||||
{
|
||||
char msg[32];
|
||||
int len;
|
||||
|
||||
snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
|
||||
if (!dp->timer->running)
|
||||
len = snd_seq_oss_timer_start(dp->timer);
|
||||
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
|
||||
snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
|
||||
ev->data.ext.ptr, ev->data.ext.len);
|
||||
} else {
|
||||
len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
|
||||
if (len > 0)
|
||||
snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* dump midi data
|
||||
* return 0 : enqueued
|
||||
* non-zero : invalid - ignored
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return -ENODEV;
|
||||
if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
|
||||
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* create OSS compatible midi_info record
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf)
|
||||
{
|
||||
seq_oss_midi_t *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
return -ENXIO;
|
||||
inf->device = dev;
|
||||
inf->dev_type = 0; /* FIXME: ?? */
|
||||
inf->capabilities = 0; /* FIXME: ?? */
|
||||
strlcpy(inf->name, mdev->name, sizeof(inf->name));
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* proc interface
|
||||
*/
|
||||
static char *
|
||||
capmode_str(int val)
|
||||
{
|
||||
val &= PERM_READ|PERM_WRITE;
|
||||
if (val == (PERM_READ|PERM_WRITE))
|
||||
return "read/write";
|
||||
else if (val == PERM_READ)
|
||||
return "read";
|
||||
else if (val == PERM_WRITE)
|
||||
return "write";
|
||||
else
|
||||
return "none";
|
||||
}
|
||||
|
||||
void
|
||||
snd_seq_oss_midi_info_read(snd_info_buffer_t *buf)
|
||||
{
|
||||
int i;
|
||||
seq_oss_midi_t *mdev;
|
||||
|
||||
snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
|
||||
for (i = 0; i < max_midi_devs; i++) {
|
||||
snd_iprintf(buf, "\nmidi %d: ", i);
|
||||
mdev = get_mdev(i);
|
||||
if (mdev == NULL) {
|
||||
snd_iprintf(buf, "*empty*\n");
|
||||
continue;
|
||||
}
|
||||
snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
|
||||
mdev->client, mdev->port);
|
||||
snd_iprintf(buf, " capability %s / opened %s\n",
|
||||
capmode_str(mdev->flags),
|
||||
capmode_str(mdev->opened));
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
}
|
||||
}
|
||||
|
||||
49
sound/core/seq/oss/seq_oss_midi.h
Normal file
49
sound/core/seq/oss/seq_oss_midi.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* midi device information
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_MIDI_H
|
||||
#define __SEQ_OSS_MIDI_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
|
||||
typedef struct seq_oss_midi_t seq_oss_midi_t;
|
||||
|
||||
int snd_seq_oss_midi_lookup_ports(int client);
|
||||
int snd_seq_oss_midi_check_new_port(snd_seq_port_info_t *pinfo);
|
||||
int snd_seq_oss_midi_check_exit_port(int client, int port);
|
||||
void snd_seq_oss_midi_clear_all(void);
|
||||
|
||||
void snd_seq_oss_midi_setup(seq_oss_devinfo_t *dp);
|
||||
void snd_seq_oss_midi_cleanup(seq_oss_devinfo_t *dp);
|
||||
|
||||
int snd_seq_oss_midi_open(seq_oss_devinfo_t *dp, int dev, int file_mode);
|
||||
void snd_seq_oss_midi_open_all(seq_oss_devinfo_t *dp, int file_mode);
|
||||
int snd_seq_oss_midi_close(seq_oss_devinfo_t *dp, int dev);
|
||||
void snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev);
|
||||
int snd_seq_oss_midi_putc(seq_oss_devinfo_t *dp, int dev, unsigned char c, snd_seq_event_t *ev);
|
||||
int snd_seq_oss_midi_input(snd_seq_event_t *ev, int direct, void *private);
|
||||
int snd_seq_oss_midi_filemode(seq_oss_devinfo_t *dp, int dev);
|
||||
int snd_seq_oss_midi_make_info(seq_oss_devinfo_t *dp, int dev, struct midi_info *inf);
|
||||
void snd_seq_oss_midi_get_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_addr_t *addr);
|
||||
|
||||
#endif
|
||||
234
sound/core/seq/oss/seq_oss_readq.c
Normal file
234
sound/core/seq/oss/seq_oss_readq.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* seq_oss_readq.c - MIDI input queue
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
#include "../seq_lock.h"
|
||||
#include <linux/wait.h>
|
||||
|
||||
/*
|
||||
* constants
|
||||
*/
|
||||
//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1)
|
||||
#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600)
|
||||
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* create a read queue
|
||||
*/
|
||||
seq_oss_readq_t *
|
||||
snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen)
|
||||
{
|
||||
seq_oss_readq_t *q;
|
||||
|
||||
if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't malloc read queue\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((q->q = kcalloc(maxlen, sizeof(evrec_t), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't malloc read queue buffer\n");
|
||||
kfree(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
q->maxlen = maxlen;
|
||||
q->qlen = 0;
|
||||
q->head = q->tail = 0;
|
||||
init_waitqueue_head(&q->midi_sleep);
|
||||
spin_lock_init(&q->lock);
|
||||
q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
|
||||
q->input_time = (unsigned long)-1;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete the read queue
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_readq_delete(seq_oss_readq_t *q)
|
||||
{
|
||||
if (q) {
|
||||
kfree(q->q);
|
||||
kfree(q);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* reset the read queue
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_readq_clear(seq_oss_readq_t *q)
|
||||
{
|
||||
if (q->qlen) {
|
||||
q->qlen = 0;
|
||||
q->head = q->tail = 0;
|
||||
}
|
||||
/* if someone sleeping, wake'em up */
|
||||
if (waitqueue_active(&q->midi_sleep))
|
||||
wake_up(&q->midi_sleep);
|
||||
q->input_time = (unsigned long)-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* put a midi byte
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_readq_puts(seq_oss_readq_t *q, int dev, unsigned char *data, int len)
|
||||
{
|
||||
evrec_t rec;
|
||||
int result;
|
||||
|
||||
memset(&rec, 0, sizeof(rec));
|
||||
rec.c[0] = SEQ_MIDIPUTC;
|
||||
rec.c[2] = dev;
|
||||
|
||||
while (len-- > 0) {
|
||||
rec.c[1] = *data++;
|
||||
result = snd_seq_oss_readq_put_event(q, &rec);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy an event to input queue:
|
||||
* return zero if enqueued
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&q->lock, flags);
|
||||
if (q->qlen >= q->maxlen - 1) {
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(&q->q[q->tail], ev, sizeof(*ev));
|
||||
q->tail = (q->tail + 1) % q->maxlen;
|
||||
q->qlen++;
|
||||
|
||||
/* wake up sleeper */
|
||||
if (waitqueue_active(&q->midi_sleep))
|
||||
wake_up(&q->midi_sleep);
|
||||
|
||||
spin_unlock_irqrestore(&q->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pop queue
|
||||
* caller must hold lock
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec)
|
||||
{
|
||||
if (q->qlen == 0)
|
||||
return -EAGAIN;
|
||||
memcpy(rec, &q->q[q->head], sizeof(*rec));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sleep until ready
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_readq_wait(seq_oss_readq_t *q)
|
||||
{
|
||||
wait_event_interruptible_timeout(q->midi_sleep,
|
||||
(q->qlen > 0 || q->head == q->tail),
|
||||
q->pre_event_timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* drain one record
|
||||
* caller must hold lock
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_readq_free(seq_oss_readq_t *q)
|
||||
{
|
||||
if (q->qlen > 0) {
|
||||
q->head = (q->head + 1) % q->maxlen;
|
||||
q->qlen--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* polling/select:
|
||||
* return non-zero if readq is not empty.
|
||||
*/
|
||||
unsigned int
|
||||
snd_seq_oss_readq_poll(seq_oss_readq_t *q, struct file *file, poll_table *wait)
|
||||
{
|
||||
poll_wait(file, &q->midi_sleep, wait);
|
||||
return q->qlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* put a timestamp
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *q, unsigned long curt, int seq_mode)
|
||||
{
|
||||
if (curt != q->input_time) {
|
||||
evrec_t rec;
|
||||
memset(&rec, 0, sizeof(rec));
|
||||
switch (seq_mode) {
|
||||
case SNDRV_SEQ_OSS_MODE_SYNTH:
|
||||
rec.echo = (curt << 8) | SEQ_WAIT;
|
||||
snd_seq_oss_readq_put_event(q, &rec);
|
||||
break;
|
||||
case SNDRV_SEQ_OSS_MODE_MUSIC:
|
||||
rec.t.code = EV_TIMING;
|
||||
rec.t.cmd = TMR_WAIT_ABS;
|
||||
rec.t.time = curt;
|
||||
snd_seq_oss_readq_put_event(q, &rec);
|
||||
break;
|
||||
}
|
||||
q->input_time = curt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* proc interface
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf)
|
||||
{
|
||||
snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n",
|
||||
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
|
||||
q->qlen, q->input_time);
|
||||
}
|
||||
56
sound/core/seq/oss/seq_oss_readq.h
Normal file
56
sound/core/seq/oss/seq_oss_readq.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
* read fifo queue
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_READQ_H
|
||||
#define __SEQ_OSS_READQ_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
|
||||
|
||||
/*
|
||||
* definition of read queue
|
||||
*/
|
||||
struct seq_oss_readq_t {
|
||||
evrec_t *q;
|
||||
int qlen;
|
||||
int maxlen;
|
||||
int head, tail;
|
||||
unsigned long pre_event_timeout;
|
||||
unsigned long input_time;
|
||||
wait_queue_head_t midi_sleep;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
seq_oss_readq_t *snd_seq_oss_readq_new(seq_oss_devinfo_t *dp, int maxlen);
|
||||
void snd_seq_oss_readq_delete(seq_oss_readq_t *q);
|
||||
void snd_seq_oss_readq_clear(seq_oss_readq_t *readq);
|
||||
unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, poll_table *wait);
|
||||
int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len);
|
||||
int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev);
|
||||
int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode);
|
||||
int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec);
|
||||
void snd_seq_oss_readq_wait(seq_oss_readq_t *q);
|
||||
void snd_seq_oss_readq_free(seq_oss_readq_t *q);
|
||||
|
||||
#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags)
|
||||
#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags)
|
||||
|
||||
#endif
|
||||
216
sound/core/seq/oss/seq_oss_rw.c
Normal file
216
sound/core/seq/oss/seq_oss_rw.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* read/write/select interface to device file
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include "seq_oss_readq.h"
|
||||
#include "seq_oss_writeq.h"
|
||||
#include "seq_oss_synth.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
#include "seq_oss_event.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include "../seq_clientmgr.h"
|
||||
|
||||
|
||||
/*
|
||||
* protoypes
|
||||
*/
|
||||
static int insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt);
|
||||
|
||||
|
||||
/*
|
||||
* read interface
|
||||
*/
|
||||
|
||||
int
|
||||
snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count)
|
||||
{
|
||||
seq_oss_readq_t *readq = dp->readq;
|
||||
int result = 0, err = 0;
|
||||
int ev_len;
|
||||
evrec_t rec;
|
||||
unsigned long flags;
|
||||
|
||||
if (readq == NULL || ! is_read_mode(dp->file_mode))
|
||||
return -ENXIO;
|
||||
|
||||
while (count >= SHORT_EVENT_SIZE) {
|
||||
snd_seq_oss_readq_lock(readq, flags);
|
||||
err = snd_seq_oss_readq_pick(readq, &rec);
|
||||
if (err == -EAGAIN &&
|
||||
!is_nonblock_mode(dp->file_mode) && result == 0) {
|
||||
snd_seq_oss_readq_unlock(readq, flags);
|
||||
snd_seq_oss_readq_wait(readq);
|
||||
snd_seq_oss_readq_lock(readq, flags);
|
||||
if (signal_pending(current))
|
||||
err = -ERESTARTSYS;
|
||||
else
|
||||
err = snd_seq_oss_readq_pick(readq, &rec);
|
||||
}
|
||||
if (err < 0) {
|
||||
snd_seq_oss_readq_unlock(readq, flags);
|
||||
break;
|
||||
}
|
||||
ev_len = ev_length(&rec);
|
||||
if (ev_len < count) {
|
||||
snd_seq_oss_readq_unlock(readq, flags);
|
||||
break;
|
||||
}
|
||||
snd_seq_oss_readq_free(readq);
|
||||
snd_seq_oss_readq_unlock(readq, flags);
|
||||
if (copy_to_user(buf, &rec, ev_len)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
result += ev_len;
|
||||
buf += ev_len;
|
||||
count -= ev_len;
|
||||
}
|
||||
return result > 0 ? result : err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* write interface
|
||||
*/
|
||||
|
||||
int
|
||||
snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt)
|
||||
{
|
||||
int result = 0, err = 0;
|
||||
int ev_size, fmt;
|
||||
evrec_t rec;
|
||||
|
||||
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
while (count >= SHORT_EVENT_SIZE) {
|
||||
if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (rec.s.code == SEQ_FULLSIZE) {
|
||||
/* load patch */
|
||||
if (result > 0) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
fmt = (*(unsigned short *)rec.c) & 0xffff;
|
||||
/* FIXME the return value isn't correct */
|
||||
return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
|
||||
fmt, buf, 0, count);
|
||||
}
|
||||
if (ev_is_long(&rec)) {
|
||||
/* extended code */
|
||||
if (rec.s.code == SEQ_EXTENDED &&
|
||||
dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ev_size = LONG_EVENT_SIZE;
|
||||
if (count < ev_size)
|
||||
break;
|
||||
/* copy the reset 4 bytes */
|
||||
if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
|
||||
buf + SHORT_EVENT_SIZE,
|
||||
LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* old-type code */
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ev_size = SHORT_EVENT_SIZE;
|
||||
}
|
||||
|
||||
/* insert queue */
|
||||
if ((err = insert_queue(dp, &rec, opt)) < 0)
|
||||
break;
|
||||
|
||||
result += ev_size;
|
||||
buf += ev_size;
|
||||
count -= ev_size;
|
||||
}
|
||||
return result > 0 ? result : err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* insert event record to write queue
|
||||
* return: 0 = OK, non-zero = NG
|
||||
*/
|
||||
static int
|
||||
insert_queue(seq_oss_devinfo_t *dp, evrec_t *rec, struct file *opt)
|
||||
{
|
||||
int rc = 0;
|
||||
snd_seq_event_t event;
|
||||
|
||||
/* if this is a timing event, process the current time */
|
||||
if (snd_seq_oss_process_timer_event(dp->timer, rec))
|
||||
return 0; /* no need to insert queue */
|
||||
|
||||
/* parse this event */
|
||||
memset(&event, 0, sizeof(event));
|
||||
/* set dummy -- to be sure */
|
||||
event.type = SNDRV_SEQ_EVENT_NOTEOFF;
|
||||
snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
|
||||
|
||||
if (snd_seq_oss_process_event(dp, rec, &event))
|
||||
return 0; /* invalid event - no need to insert queue */
|
||||
|
||||
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
|
||||
if (dp->timer->realtime || !dp->timer->running) {
|
||||
snd_seq_oss_dispatch(dp, &event, 0, 0);
|
||||
} else {
|
||||
if (is_nonblock_mode(dp->file_mode))
|
||||
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
|
||||
else
|
||||
rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* select / poll
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
snd_seq_oss_poll(seq_oss_devinfo_t *dp, struct file *file, poll_table * wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
|
||||
/* input */
|
||||
if (dp->readq && is_read_mode(dp->file_mode)) {
|
||||
if (snd_seq_oss_readq_poll(dp->readq, file, wait))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
/* output */
|
||||
if (dp->writeq && is_write_mode(dp->file_mode)) {
|
||||
if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
659
sound/core/seq/oss/seq_oss_synth.c
Normal file
659
sound/core/seq/oss/seq_oss_synth.c
Normal file
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* synth device handlers
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_synth.h"
|
||||
#include "seq_oss_midi.h"
|
||||
#include "../seq_lock.h"
|
||||
#include <linux/init.h>
|
||||
|
||||
/*
|
||||
* constants
|
||||
*/
|
||||
#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30
|
||||
#define MAX_SYSEX_BUFLEN 128
|
||||
|
||||
|
||||
/*
|
||||
* definition of synth info records
|
||||
*/
|
||||
|
||||
/* sysex buffer */
|
||||
struct seq_oss_synth_sysex_t {
|
||||
int len;
|
||||
int skip;
|
||||
unsigned char buf[MAX_SYSEX_BUFLEN];
|
||||
};
|
||||
|
||||
/* synth info */
|
||||
struct seq_oss_synth_t {
|
||||
int seq_device;
|
||||
|
||||
/* for synth_info */
|
||||
int synth_type;
|
||||
int synth_subtype;
|
||||
int nr_voices;
|
||||
|
||||
char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];
|
||||
snd_seq_oss_callback_t oper;
|
||||
|
||||
int opened;
|
||||
|
||||
void *private_data;
|
||||
snd_use_lock_t use_lock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* device table
|
||||
*/
|
||||
static int max_synth_devs;
|
||||
static seq_oss_synth_t *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
|
||||
static seq_oss_synth_t midi_synth_dev = {
|
||||
-1, /* seq_device */
|
||||
SYNTH_TYPE_MIDI, /* synth_type */
|
||||
0, /* synth_subtype */
|
||||
16, /* nr_voices */
|
||||
"MIDI", /* name */
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(register_lock);
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static seq_oss_synth_t *get_synthdev(seq_oss_devinfo_t *dp, int dev);
|
||||
static void reset_channels(seq_oss_synthinfo_t *info);
|
||||
|
||||
/*
|
||||
* global initialization
|
||||
*/
|
||||
void __init
|
||||
snd_seq_oss_synth_init(void)
|
||||
{
|
||||
snd_use_lock_init(&midi_synth_dev.use_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* registration of the synth device
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_register(snd_seq_device_t *dev)
|
||||
{
|
||||
int i;
|
||||
seq_oss_synth_t *rec;
|
||||
snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "can't malloc synth info\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
rec->seq_device = -1;
|
||||
rec->synth_type = reg->type;
|
||||
rec->synth_subtype = reg->subtype;
|
||||
rec->nr_voices = reg->nvoices;
|
||||
rec->oper = reg->oper;
|
||||
rec->private_data = reg->private_data;
|
||||
rec->opened = 0;
|
||||
snd_use_lock_init(&rec->use_lock);
|
||||
|
||||
/* copy and truncate the name of synth device */
|
||||
strlcpy(rec->name, dev->name, sizeof(rec->name));
|
||||
|
||||
/* registration */
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (i = 0; i < max_synth_devs; i++) {
|
||||
if (synth_devs[i] == NULL)
|
||||
break;
|
||||
}
|
||||
if (i >= max_synth_devs) {
|
||||
if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
snd_printk(KERN_ERR "no more synth slot\n");
|
||||
kfree(rec);
|
||||
return -ENOMEM;
|
||||
}
|
||||
max_synth_devs++;
|
||||
}
|
||||
rec->seq_device = i;
|
||||
synth_devs[i] = rec;
|
||||
debug_printk(("synth %s registered %d\n", rec->name, i));
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
dev->driver_data = rec;
|
||||
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
|
||||
if (i < SNDRV_CARDS)
|
||||
snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
snd_seq_oss_synth_unregister(snd_seq_device_t *dev)
|
||||
{
|
||||
int index;
|
||||
seq_oss_synth_t *rec = dev->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (index = 0; index < max_synth_devs; index++) {
|
||||
if (synth_devs[index] == rec)
|
||||
break;
|
||||
}
|
||||
if (index >= max_synth_devs) {
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
snd_printk(KERN_ERR "can't unregister synth\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
synth_devs[index] = NULL;
|
||||
if (index == max_synth_devs - 1) {
|
||||
for (index--; index >= 0; index--) {
|
||||
if (synth_devs[index])
|
||||
break;
|
||||
}
|
||||
max_synth_devs = index + 1;
|
||||
}
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
|
||||
if (rec->seq_device < SNDRV_CARDS)
|
||||
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
|
||||
#endif
|
||||
|
||||
snd_use_lock_sync(&rec->use_lock);
|
||||
kfree(rec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
static seq_oss_synth_t *
|
||||
get_sdev(int dev)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
rec = synth_devs[dev];
|
||||
if (rec)
|
||||
snd_use_lock_use(&rec->use_lock);
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set up synth tables
|
||||
*/
|
||||
|
||||
void
|
||||
snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int i;
|
||||
seq_oss_synth_t *rec;
|
||||
seq_oss_synthinfo_t *info;
|
||||
|
||||
dp->max_synthdev = max_synth_devs;
|
||||
dp->synth_opened = 0;
|
||||
memset(dp->synths, 0, sizeof(dp->synths));
|
||||
for (i = 0; i < dp->max_synthdev; i++) {
|
||||
rec = get_sdev(i);
|
||||
if (rec == NULL)
|
||||
continue;
|
||||
if (rec->oper.open == NULL || rec->oper.close == NULL) {
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
continue;
|
||||
}
|
||||
info = &dp->synths[i];
|
||||
info->arg.app_index = dp->port;
|
||||
info->arg.file_mode = dp->file_mode;
|
||||
info->arg.seq_mode = dp->seq_mode;
|
||||
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
|
||||
info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
|
||||
else
|
||||
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
|
||||
info->opened = 0;
|
||||
if (!try_module_get(rec->oper.owner)) {
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
continue;
|
||||
}
|
||||
if (rec->oper.open(&info->arg, rec->private_data) < 0) {
|
||||
module_put(rec->oper.owner);
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
continue;
|
||||
}
|
||||
info->nr_voices = rec->nr_voices;
|
||||
if (info->nr_voices > 0) {
|
||||
info->ch = kcalloc(info->nr_voices, sizeof(seq_oss_chinfo_t), GFP_KERNEL);
|
||||
if (!info->ch)
|
||||
BUG();
|
||||
reset_channels(info);
|
||||
}
|
||||
debug_printk(("synth %d assigned\n", i));
|
||||
info->opened++;
|
||||
rec->opened++;
|
||||
dp->synth_opened++;
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set up synth tables for MIDI emulation - /dev/music mode only
|
||||
*/
|
||||
|
||||
void
|
||||
snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
|
||||
return;
|
||||
|
||||
for (i = 0; i < dp->max_mididev; i++) {
|
||||
seq_oss_synthinfo_t *info;
|
||||
info = &dp->synths[dp->max_synthdev];
|
||||
if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)
|
||||
continue;
|
||||
info->arg.app_index = dp->port;
|
||||
info->arg.file_mode = dp->file_mode;
|
||||
info->arg.seq_mode = dp->seq_mode;
|
||||
info->arg.private_data = info;
|
||||
info->is_midi = 1;
|
||||
info->midi_mapped = i;
|
||||
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
|
||||
snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);
|
||||
info->opened = 1;
|
||||
midi_synth_dev.opened++;
|
||||
dp->max_synthdev++;
|
||||
if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clean up synth tables
|
||||
*/
|
||||
|
||||
void
|
||||
snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
int i;
|
||||
seq_oss_synth_t *rec;
|
||||
seq_oss_synthinfo_t *info;
|
||||
|
||||
snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return);
|
||||
for (i = 0; i < dp->max_synthdev; i++) {
|
||||
info = &dp->synths[i];
|
||||
if (! info->opened)
|
||||
continue;
|
||||
if (info->is_midi) {
|
||||
if (midi_synth_dev.opened > 0) {
|
||||
snd_seq_oss_midi_close(dp, info->midi_mapped);
|
||||
midi_synth_dev.opened--;
|
||||
}
|
||||
} else {
|
||||
rec = get_sdev(i);
|
||||
if (rec == NULL)
|
||||
continue;
|
||||
if (rec->opened > 0) {
|
||||
debug_printk(("synth %d closed\n", i));
|
||||
rec->oper.close(&info->arg);
|
||||
module_put(rec->oper.owner);
|
||||
rec->opened = 0;
|
||||
}
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
if (info->sysex) {
|
||||
kfree(info->sysex);
|
||||
info->sysex = NULL;
|
||||
}
|
||||
if (info->ch) {
|
||||
kfree(info->ch);
|
||||
info->ch = NULL;
|
||||
}
|
||||
}
|
||||
dp->synth_opened = 0;
|
||||
dp->max_synthdev = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if the specified device is MIDI mapped device
|
||||
*/
|
||||
static int
|
||||
is_midi_dev(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
if (dev < 0 || dev >= dp->max_synthdev)
|
||||
return 0;
|
||||
if (dp->synths[dev].is_midi)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return synth device information pointer
|
||||
*/
|
||||
static seq_oss_synth_t *
|
||||
get_synthdev(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
if (dev < 0 || dev >= dp->max_synthdev)
|
||||
return NULL;
|
||||
if (! dp->synths[dev].opened)
|
||||
return NULL;
|
||||
if (dp->synths[dev].is_midi)
|
||||
return &midi_synth_dev;
|
||||
if ((rec = get_sdev(dev)) == NULL)
|
||||
return NULL;
|
||||
if (! rec->opened) {
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
return NULL;
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset note and velocity on each channel.
|
||||
*/
|
||||
static void
|
||||
reset_channels(seq_oss_synthinfo_t *info)
|
||||
{
|
||||
int i;
|
||||
if (info->ch == NULL || ! info->nr_voices)
|
||||
return;
|
||||
for (i = 0; i < info->nr_voices; i++) {
|
||||
info->ch[i].note = -1;
|
||||
info->ch[i].vel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset synth device:
|
||||
* call reset callback. if no callback is defined, send a heartbeat
|
||||
* event to the corresponding port.
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
seq_oss_synthinfo_t *info;
|
||||
|
||||
snd_assert(dev >= 0 && dev < dp->max_synthdev, return);
|
||||
info = &dp->synths[dev];
|
||||
if (! info->opened)
|
||||
return;
|
||||
if (info->sysex)
|
||||
info->sysex->len = 0; /* reset sysex */
|
||||
reset_channels(info);
|
||||
if (info->is_midi) {
|
||||
if (midi_synth_dev.opened <= 0)
|
||||
return;
|
||||
snd_seq_oss_midi_reset(dp, info->midi_mapped);
|
||||
/* reopen the device */
|
||||
snd_seq_oss_midi_close(dp, dev);
|
||||
if (snd_seq_oss_midi_open(dp, info->midi_mapped,
|
||||
dp->file_mode) < 0) {
|
||||
midi_synth_dev.opened--;
|
||||
info->opened = 0;
|
||||
if (info->sysex) {
|
||||
kfree(info->sysex);
|
||||
info->sysex = NULL;
|
||||
}
|
||||
if (info->ch) {
|
||||
kfree(info->ch);
|
||||
info->ch = NULL;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
rec = get_sdev(dev);
|
||||
if (rec == NULL)
|
||||
return;
|
||||
if (rec->oper.reset) {
|
||||
rec->oper.reset(&info->arg);
|
||||
} else {
|
||||
snd_seq_event_t ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,
|
||||
info->arg.addr.port);
|
||||
ev.type = SNDRV_SEQ_EVENT_RESET;
|
||||
snd_seq_oss_dispatch(dp, &ev, 0, 0);
|
||||
}
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* load a patch record:
|
||||
* call load_patch callback function
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt,
|
||||
const char __user *buf, int p, int c)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
int rc;
|
||||
|
||||
if (dev < 0 || dev >= dp->max_synthdev)
|
||||
return -ENXIO;
|
||||
|
||||
if (is_midi_dev(dp, dev))
|
||||
return 0;
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
if (rec->oper.load_patch == NULL)
|
||||
rc = -ENXIO;
|
||||
else
|
||||
rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if the device is valid synth device
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
rec = get_synthdev(dp, dev);
|
||||
if (rec) {
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* receive OSS 6 byte sysex packet:
|
||||
* the full sysex message will be sent if it reaches to the end of data
|
||||
* (0xff).
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev)
|
||||
{
|
||||
int i, send;
|
||||
unsigned char *dest;
|
||||
seq_oss_synth_sysex_t *sysex;
|
||||
|
||||
if (! snd_seq_oss_synth_is_valid(dp, dev))
|
||||
return -ENXIO;
|
||||
|
||||
sysex = dp->synths[dev].sysex;
|
||||
if (sysex == NULL) {
|
||||
sysex = kcalloc(1, sizeof(*sysex), GFP_KERNEL);
|
||||
if (sysex == NULL)
|
||||
return -ENOMEM;
|
||||
dp->synths[dev].sysex = sysex;
|
||||
}
|
||||
|
||||
send = 0;
|
||||
dest = sysex->buf + sysex->len;
|
||||
/* copy 6 byte packet to the buffer */
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (buf[i] == 0xff) {
|
||||
send = 1;
|
||||
break;
|
||||
}
|
||||
dest[i] = buf[i];
|
||||
sysex->len++;
|
||||
if (sysex->len >= MAX_SYSEX_BUFLEN) {
|
||||
sysex->len = 0;
|
||||
sysex->skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysex->len && send) {
|
||||
if (sysex->skip) {
|
||||
sysex->skip = 0;
|
||||
sysex->len = 0;
|
||||
return -EINVAL; /* skip */
|
||||
}
|
||||
/* copy the data to event record and send it */
|
||||
ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
|
||||
if (snd_seq_oss_synth_addr(dp, dev, ev))
|
||||
return -EINVAL;
|
||||
ev->data.ext.len = sysex->len;
|
||||
ev->data.ext.ptr = sysex->buf;
|
||||
sysex->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL; /* skip */
|
||||
}
|
||||
|
||||
/*
|
||||
* fill the event source/destination addresses
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev)
|
||||
{
|
||||
if (! snd_seq_oss_synth_is_valid(dp, dev))
|
||||
return -EINVAL;
|
||||
snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
|
||||
dp->synths[dev].arg.addr.port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* OSS compatible ioctl
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
int rc;
|
||||
|
||||
if (is_midi_dev(dp, dev))
|
||||
return -ENXIO;
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
return -ENXIO;
|
||||
if (rec->oper.ioctl == NULL)
|
||||
rc = -ENXIO;
|
||||
else
|
||||
rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev)
|
||||
{
|
||||
if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
|
||||
return -ENXIO;
|
||||
ev->type = SNDRV_SEQ_EVENT_OSS;
|
||||
memcpy(ev->data.raw8.d, data, 8);
|
||||
return snd_seq_oss_synth_addr(dp, dev, ev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create OSS compatible synth_info record
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf)
|
||||
{
|
||||
seq_oss_synth_t *rec;
|
||||
|
||||
if (dp->synths[dev].is_midi) {
|
||||
struct midi_info minf;
|
||||
snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
|
||||
inf->synth_type = SYNTH_TYPE_MIDI;
|
||||
inf->synth_subtype = 0;
|
||||
inf->nr_voices = 16;
|
||||
inf->device = dev;
|
||||
strlcpy(inf->name, minf.name, sizeof(inf->name));
|
||||
} else {
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
return -ENXIO;
|
||||
inf->synth_type = rec->synth_type;
|
||||
inf->synth_subtype = rec->synth_subtype;
|
||||
inf->nr_voices = rec->nr_voices;
|
||||
inf->device = dev;
|
||||
strlcpy(inf->name, rec->name, sizeof(inf->name));
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* proc interface
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_synth_info_read(snd_info_buffer_t *buf)
|
||||
{
|
||||
int i;
|
||||
seq_oss_synth_t *rec;
|
||||
|
||||
snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
|
||||
for (i = 0; i < max_synth_devs; i++) {
|
||||
snd_iprintf(buf, "\nsynth %d: ", i);
|
||||
rec = get_sdev(i);
|
||||
if (rec == NULL) {
|
||||
snd_iprintf(buf, "*empty*\n");
|
||||
continue;
|
||||
}
|
||||
snd_iprintf(buf, "[%s]\n", rec->name);
|
||||
snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n",
|
||||
rec->synth_type, rec->synth_subtype,
|
||||
rec->nr_voices);
|
||||
snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
|
||||
enabled_str((long)rec->oper.ioctl),
|
||||
enabled_str((long)rec->oper.load_patch));
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
}
|
||||
|
||||
49
sound/core/seq/oss/seq_oss_synth.h
Normal file
49
sound/core/seq/oss/seq_oss_synth.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* synth device information
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_SYNTH_H
|
||||
#define __SEQ_OSS_SYNTH_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
#include <sound/seq_device.h>
|
||||
|
||||
typedef struct seq_oss_synth_t seq_oss_synth_t;
|
||||
|
||||
void snd_seq_oss_synth_init(void);
|
||||
int snd_seq_oss_synth_register(snd_seq_device_t *dev);
|
||||
int snd_seq_oss_synth_unregister(snd_seq_device_t *dev);
|
||||
void snd_seq_oss_synth_setup(seq_oss_devinfo_t *dp);
|
||||
void snd_seq_oss_synth_setup_midi(seq_oss_devinfo_t *dp);
|
||||
void snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp);
|
||||
|
||||
void snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev);
|
||||
int snd_seq_oss_synth_load_patch(seq_oss_devinfo_t *dp, int dev, int fmt, const char __user *buf, int p, int c);
|
||||
int snd_seq_oss_synth_is_valid(seq_oss_devinfo_t *dp, int dev);
|
||||
int snd_seq_oss_synth_sysex(seq_oss_devinfo_t *dp, int dev, unsigned char *buf, snd_seq_event_t *ev);
|
||||
int snd_seq_oss_synth_addr(seq_oss_devinfo_t *dp, int dev, snd_seq_event_t *ev);
|
||||
int snd_seq_oss_synth_ioctl(seq_oss_devinfo_t *dp, int dev, unsigned int cmd, unsigned long addr);
|
||||
int snd_seq_oss_synth_raw_event(seq_oss_devinfo_t *dp, int dev, unsigned char *data, snd_seq_event_t *ev);
|
||||
|
||||
int snd_seq_oss_synth_make_info(seq_oss_devinfo_t *dp, int dev, struct synth_info *inf);
|
||||
|
||||
#endif
|
||||
283
sound/core/seq/oss/seq_oss_timer.c
Normal file
283
sound/core/seq/oss/seq_oss_timer.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* Timer control routines
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_timer.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
|
||||
/*
|
||||
*/
|
||||
#define MIN_OSS_TEMPO 8
|
||||
#define MAX_OSS_TEMPO 360
|
||||
#define MIN_OSS_TIMEBASE 1
|
||||
#define MAX_OSS_TIMEBASE 1000
|
||||
|
||||
/*
|
||||
*/
|
||||
static void calc_alsa_tempo(seq_oss_timer_t *timer);
|
||||
static int send_timer_event(seq_oss_devinfo_t *dp, int type, int value);
|
||||
|
||||
|
||||
/*
|
||||
* create and register a new timer.
|
||||
* if queue is not started yet, start it.
|
||||
*/
|
||||
seq_oss_timer_t *
|
||||
snd_seq_oss_timer_new(seq_oss_devinfo_t *dp)
|
||||
{
|
||||
seq_oss_timer_t *rec;
|
||||
|
||||
rec = kcalloc(1, sizeof(*rec), GFP_KERNEL);
|
||||
if (rec == NULL)
|
||||
return NULL;
|
||||
|
||||
rec->dp = dp;
|
||||
rec->cur_tick = 0;
|
||||
rec->realtime = 0;
|
||||
rec->running = 0;
|
||||
rec->oss_tempo = 60;
|
||||
rec->oss_timebase = 100;
|
||||
calc_alsa_tempo(rec);
|
||||
|
||||
return rec;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* delete timer.
|
||||
* if no more timer exists, stop the queue.
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_timer_delete(seq_oss_timer_t *rec)
|
||||
{
|
||||
if (rec) {
|
||||
snd_seq_oss_timer_stop(rec);
|
||||
kfree(rec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* process one timing event
|
||||
* return 1 : event proceseed -- skip this event
|
||||
* 0 : not a timer event -- enqueue this event
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_process_timer_event(seq_oss_timer_t *rec, evrec_t *ev)
|
||||
{
|
||||
abstime_t parm = ev->t.time;
|
||||
|
||||
if (ev->t.code == EV_TIMING) {
|
||||
switch (ev->t.cmd) {
|
||||
case TMR_WAIT_REL:
|
||||
parm += rec->cur_tick;
|
||||
rec->realtime = 0;
|
||||
/* continue to next */
|
||||
case TMR_WAIT_ABS:
|
||||
if (parm == 0) {
|
||||
rec->realtime = 1;
|
||||
} else if (parm >= rec->cur_tick) {
|
||||
rec->realtime = 0;
|
||||
rec->cur_tick = parm;
|
||||
}
|
||||
return 1; /* skip this event */
|
||||
|
||||
case TMR_START:
|
||||
snd_seq_oss_timer_start(rec);
|
||||
return 1;
|
||||
|
||||
}
|
||||
} else if (ev->s.code == SEQ_WAIT) {
|
||||
/* time = from 1 to 3 bytes */
|
||||
parm = (ev->echo >> 8) & 0xffffff;
|
||||
if (parm > rec->cur_tick) {
|
||||
/* set next event time */
|
||||
rec->cur_tick = parm;
|
||||
rec->realtime = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* convert tempo units
|
||||
*/
|
||||
static void
|
||||
calc_alsa_tempo(seq_oss_timer_t *timer)
|
||||
{
|
||||
timer->tempo = (60 * 1000000) / timer->oss_tempo;
|
||||
timer->ppq = timer->oss_timebase;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* dispatch a timer event
|
||||
*/
|
||||
static int
|
||||
send_timer_event(seq_oss_devinfo_t *dp, int type, int value)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = type;
|
||||
ev.source.client = dp->cseq;
|
||||
ev.source.port = 0;
|
||||
ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
|
||||
ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
|
||||
ev.queue = dp->queue;
|
||||
ev.data.queue.queue = dp->queue;
|
||||
ev.data.queue.param.value = value;
|
||||
return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* set queue tempo and start queue
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_timer_start(seq_oss_timer_t *timer)
|
||||
{
|
||||
seq_oss_devinfo_t *dp = timer->dp;
|
||||
snd_seq_queue_tempo_t tmprec;
|
||||
|
||||
if (timer->running)
|
||||
snd_seq_oss_timer_stop(timer);
|
||||
|
||||
memset(&tmprec, 0, sizeof(tmprec));
|
||||
tmprec.queue = dp->queue;
|
||||
tmprec.ppq = timer->ppq;
|
||||
tmprec.tempo = timer->tempo;
|
||||
snd_seq_set_queue_tempo(dp->cseq, &tmprec);
|
||||
|
||||
send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
|
||||
timer->running = 1;
|
||||
timer->cur_tick = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* stop queue
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_timer_stop(seq_oss_timer_t *timer)
|
||||
{
|
||||
if (! timer->running)
|
||||
return 0;
|
||||
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
|
||||
timer->running = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* continue queue
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_timer_continue(seq_oss_timer_t *timer)
|
||||
{
|
||||
if (timer->running)
|
||||
return 0;
|
||||
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
|
||||
timer->running = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* change queue tempo
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value)
|
||||
{
|
||||
if (value < MIN_OSS_TEMPO)
|
||||
value = MIN_OSS_TEMPO;
|
||||
else if (value > MAX_OSS_TEMPO)
|
||||
value = MAX_OSS_TEMPO;
|
||||
timer->oss_tempo = value;
|
||||
calc_alsa_tempo(timer);
|
||||
if (timer->running)
|
||||
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ioctls
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (cmd == SNDCTL_SEQ_CTRLRATE) {
|
||||
debug_printk(("ctrl rate\n"));
|
||||
/* if *arg == 0, just return the current rate */
|
||||
if (get_user(value, arg))
|
||||
return -EFAULT;
|
||||
if (value)
|
||||
return -EINVAL;
|
||||
value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
|
||||
return put_user(value, arg) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
|
||||
return 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDCTL_TMR_START:
|
||||
debug_printk(("timer start\n"));
|
||||
return snd_seq_oss_timer_start(timer);
|
||||
case SNDCTL_TMR_STOP:
|
||||
debug_printk(("timer stop\n"));
|
||||
return snd_seq_oss_timer_stop(timer);
|
||||
case SNDCTL_TMR_CONTINUE:
|
||||
debug_printk(("timer continue\n"));
|
||||
return snd_seq_oss_timer_continue(timer);
|
||||
case SNDCTL_TMR_TEMPO:
|
||||
debug_printk(("timer tempo\n"));
|
||||
if (get_user(value, arg))
|
||||
return -EFAULT;
|
||||
return snd_seq_oss_timer_tempo(timer, value);
|
||||
case SNDCTL_TMR_TIMEBASE:
|
||||
debug_printk(("timer timebase\n"));
|
||||
if (get_user(value, arg))
|
||||
return -EFAULT;
|
||||
if (value < MIN_OSS_TIMEBASE)
|
||||
value = MIN_OSS_TIMEBASE;
|
||||
else if (value > MAX_OSS_TIMEBASE)
|
||||
value = MAX_OSS_TIMEBASE;
|
||||
timer->oss_timebase = value;
|
||||
calc_alsa_tempo(timer);
|
||||
return 0;
|
||||
|
||||
case SNDCTL_TMR_METRONOME:
|
||||
case SNDCTL_TMR_SELECT:
|
||||
case SNDCTL_TMR_SOURCE:
|
||||
debug_printk(("timer XXX\n"));
|
||||
/* not supported */
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
70
sound/core/seq/oss/seq_oss_timer.h
Normal file
70
sound/core/seq/oss/seq_oss_timer.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
* timer handling routines
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_TIMER_H
|
||||
#define __SEQ_OSS_TIMER_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
|
||||
/*
|
||||
* timer information definition
|
||||
*/
|
||||
struct seq_oss_timer_t {
|
||||
seq_oss_devinfo_t *dp;
|
||||
reltime_t cur_tick;
|
||||
int realtime;
|
||||
int running;
|
||||
int tempo, ppq; /* ALSA queue */
|
||||
int oss_tempo, oss_timebase;
|
||||
};
|
||||
|
||||
|
||||
seq_oss_timer_t *snd_seq_oss_timer_new(seq_oss_devinfo_t *dp);
|
||||
void snd_seq_oss_timer_delete(seq_oss_timer_t *dp);
|
||||
|
||||
int snd_seq_oss_timer_start(seq_oss_timer_t *timer);
|
||||
int snd_seq_oss_timer_stop(seq_oss_timer_t *timer);
|
||||
int snd_seq_oss_timer_continue(seq_oss_timer_t *timer);
|
||||
int snd_seq_oss_timer_tempo(seq_oss_timer_t *timer, int value);
|
||||
#define snd_seq_oss_timer_reset snd_seq_oss_timer_start
|
||||
|
||||
int snd_seq_oss_timer_ioctl(seq_oss_timer_t *timer, unsigned int cmd, int __user *arg);
|
||||
|
||||
/*
|
||||
* get current processed time
|
||||
*/
|
||||
static inline abstime_t
|
||||
snd_seq_oss_timer_cur_tick(seq_oss_timer_t *timer)
|
||||
{
|
||||
return timer->cur_tick;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* is realtime event?
|
||||
*/
|
||||
static inline int
|
||||
snd_seq_oss_timer_is_realtime(seq_oss_timer_t *timer)
|
||||
{
|
||||
return timer->realtime;
|
||||
}
|
||||
|
||||
#endif
|
||||
170
sound/core/seq/oss/seq_oss_writeq.c
Normal file
170
sound/core/seq/oss/seq_oss_writeq.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
*
|
||||
* seq_oss_writeq.c - write queue and sync
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "seq_oss_writeq.h"
|
||||
#include "seq_oss_event.h"
|
||||
#include "seq_oss_timer.h"
|
||||
#include <sound/seq_oss_legacy.h>
|
||||
#include "../seq_lock.h"
|
||||
#include "../seq_clientmgr.h"
|
||||
#include <linux/wait.h>
|
||||
|
||||
|
||||
/*
|
||||
* create a write queue record
|
||||
*/
|
||||
seq_oss_writeq_t *
|
||||
snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen)
|
||||
{
|
||||
seq_oss_writeq_t *q;
|
||||
snd_seq_client_pool_t pool;
|
||||
|
||||
if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
q->dp = dp;
|
||||
q->maxlen = maxlen;
|
||||
spin_lock_init(&q->sync_lock);
|
||||
q->sync_event_put = 0;
|
||||
q->sync_time = 0;
|
||||
init_waitqueue_head(&q->sync_sleep);
|
||||
|
||||
memset(&pool, 0, sizeof(pool));
|
||||
pool.client = dp->cseq;
|
||||
pool.output_pool = maxlen;
|
||||
pool.output_room = maxlen / 2;
|
||||
|
||||
snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete the write queue
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_writeq_delete(seq_oss_writeq_t *q)
|
||||
{
|
||||
snd_seq_oss_writeq_clear(q); /* to be sure */
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset the write queue
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_writeq_clear(seq_oss_writeq_t *q)
|
||||
{
|
||||
snd_seq_remove_events_t reset;
|
||||
|
||||
memset(&reset, 0, sizeof(reset));
|
||||
reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
|
||||
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
|
||||
|
||||
/* wake up sleepers if any */
|
||||
snd_seq_oss_writeq_wakeup(q, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait until the write buffer has enough room
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_writeq_sync(seq_oss_writeq_t *q)
|
||||
{
|
||||
seq_oss_devinfo_t *dp = q->dp;
|
||||
abstime_t time;
|
||||
|
||||
time = snd_seq_oss_timer_cur_tick(dp->timer);
|
||||
if (q->sync_time >= time)
|
||||
return 0; /* already finished */
|
||||
|
||||
if (! q->sync_event_put) {
|
||||
snd_seq_event_t ev;
|
||||
evrec_t *rec;
|
||||
|
||||
/* put echoback event */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.flags = 0;
|
||||
ev.type = SNDRV_SEQ_EVENT_ECHO;
|
||||
ev.time.tick = time;
|
||||
/* echo back to itself */
|
||||
snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
|
||||
rec = (evrec_t*)&ev.data;
|
||||
rec->t.code = SEQ_SYNCTIMER;
|
||||
rec->t.time = time;
|
||||
q->sync_event_put = 1;
|
||||
snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
|
||||
}
|
||||
|
||||
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
|
||||
if (signal_pending(current))
|
||||
/* interrupted - return 0 to finish sync */
|
||||
q->sync_event_put = 0;
|
||||
if (! q->sync_event_put || q->sync_time >= time)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* wake up sync - echo event was catched
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&q->sync_lock, flags);
|
||||
q->sync_time = time;
|
||||
q->sync_event_put = 0;
|
||||
if (waitqueue_active(&q->sync_sleep)) {
|
||||
wake_up(&q->sync_sleep);
|
||||
}
|
||||
spin_unlock_irqrestore(&q->sync_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* return the unused pool size
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q)
|
||||
{
|
||||
snd_seq_client_pool_t pool;
|
||||
pool.client = q->dp->cseq;
|
||||
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
|
||||
return pool.output_free;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* set output threshold size from ioctl
|
||||
*/
|
||||
void
|
||||
snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int val)
|
||||
{
|
||||
snd_seq_client_pool_t pool;
|
||||
pool.client = q->dp->cseq;
|
||||
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
|
||||
pool.output_room = val;
|
||||
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
|
||||
}
|
||||
|
||||
50
sound/core/seq/oss/seq_oss_writeq.h
Normal file
50
sound/core/seq/oss/seq_oss_writeq.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* OSS compatible sequencer driver
|
||||
* write priority queue
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __SEQ_OSS_WRITEQ_H
|
||||
#define __SEQ_OSS_WRITEQ_H
|
||||
|
||||
#include "seq_oss_device.h"
|
||||
|
||||
|
||||
struct seq_oss_writeq_t {
|
||||
seq_oss_devinfo_t *dp;
|
||||
int maxlen;
|
||||
abstime_t sync_time;
|
||||
int sync_event_put;
|
||||
wait_queue_head_t sync_sleep;
|
||||
spinlock_t sync_lock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* seq_oss_writeq.c
|
||||
*/
|
||||
seq_oss_writeq_t *snd_seq_oss_writeq_new(seq_oss_devinfo_t *dp, int maxlen);
|
||||
void snd_seq_oss_writeq_delete(seq_oss_writeq_t *q);
|
||||
void snd_seq_oss_writeq_clear(seq_oss_writeq_t *q);
|
||||
int snd_seq_oss_writeq_sync(seq_oss_writeq_t *q);
|
||||
void snd_seq_oss_writeq_wakeup(seq_oss_writeq_t *q, abstime_t time);
|
||||
int snd_seq_oss_writeq_get_free_size(seq_oss_writeq_t *q);
|
||||
void snd_seq_oss_writeq_set_output(seq_oss_writeq_t *q, int size);
|
||||
|
||||
|
||||
#endif
|
||||
147
sound/core/seq/seq.c
Normal file
147
sound/core/seq/seq.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* ALSA sequencer main module
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
#include "seq_clientmgr.h"
|
||||
#include "seq_memory.h"
|
||||
#include "seq_queue.h"
|
||||
#include "seq_lock.h"
|
||||
#include "seq_timer.h"
|
||||
#include "seq_system.h"
|
||||
#include "seq_info.h"
|
||||
#include <sound/seq_device.h>
|
||||
|
||||
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
|
||||
int seq_client_load[64] = {[0] = SNDRV_SEQ_CLIENT_DUMMY, [1 ... 63] = -1};
|
||||
#else
|
||||
int seq_client_load[64] = {[0 ... 63] = -1};
|
||||
#endif
|
||||
int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
|
||||
int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
|
||||
int seq_default_timer_card = -1;
|
||||
int seq_default_timer_device = SNDRV_TIMER_GLOBAL_SYSTEM;
|
||||
int seq_default_timer_subdevice = 0;
|
||||
int seq_default_timer_resolution = 0; /* Hz */
|
||||
|
||||
MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param_array(seq_client_load, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(seq_client_load, "The numbers of global (system) clients to load through kmod.");
|
||||
module_param(seq_default_timer_class, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_class, "The default timer class.");
|
||||
module_param(seq_default_timer_sclass, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_sclass, "The default timer slave class.");
|
||||
module_param(seq_default_timer_card, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_card, "The default timer card number.");
|
||||
module_param(seq_default_timer_device, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_device, "The default timer device number.");
|
||||
module_param(seq_default_timer_subdevice, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice number.");
|
||||
module_param(seq_default_timer_resolution, int, 0644);
|
||||
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
|
||||
|
||||
/*
|
||||
* INIT PART
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
snd_seq_autoload_lock();
|
||||
if ((err = client_init_data()) < 0)
|
||||
goto error;
|
||||
|
||||
/* init memory, room for selected events */
|
||||
if ((err = snd_sequencer_memory_init()) < 0)
|
||||
goto error;
|
||||
|
||||
/* init event queues */
|
||||
if ((err = snd_seq_queues_init()) < 0)
|
||||
goto error;
|
||||
|
||||
/* register sequencer device */
|
||||
if ((err = snd_sequencer_device_init()) < 0)
|
||||
goto error;
|
||||
|
||||
/* register proc interface */
|
||||
if ((err = snd_seq_info_init()) < 0)
|
||||
goto error;
|
||||
|
||||
/* register our internal client */
|
||||
if ((err = snd_seq_system_client_init()) < 0)
|
||||
goto error;
|
||||
|
||||
error:
|
||||
snd_seq_autoload_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_exit(void)
|
||||
{
|
||||
/* unregister our internal client */
|
||||
snd_seq_system_client_done();
|
||||
|
||||
/* unregister proc interface */
|
||||
snd_seq_info_done();
|
||||
|
||||
/* delete timing queues */
|
||||
snd_seq_queues_delete();
|
||||
|
||||
/* unregister sequencer device */
|
||||
snd_sequencer_device_done();
|
||||
|
||||
/* release event memory */
|
||||
snd_sequencer_memory_done();
|
||||
}
|
||||
|
||||
module_init(alsa_seq_init)
|
||||
module_exit(alsa_seq_exit)
|
||||
|
||||
/* seq_clientmgr.c */
|
||||
EXPORT_SYMBOL(snd_seq_create_kernel_client);
|
||||
EXPORT_SYMBOL(snd_seq_delete_kernel_client);
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
|
||||
EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
|
||||
EXPORT_SYMBOL(snd_seq_set_queue_tempo);
|
||||
/* seq_memory.c */
|
||||
EXPORT_SYMBOL(snd_seq_expand_var_event);
|
||||
EXPORT_SYMBOL(snd_seq_dump_var_event);
|
||||
/* seq_ports.c */
|
||||
EXPORT_SYMBOL(snd_seq_event_port_attach);
|
||||
EXPORT_SYMBOL(snd_seq_event_port_detach);
|
||||
/* seq_lock.c */
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
|
||||
/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/
|
||||
/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/
|
||||
EXPORT_SYMBOL(snd_use_lock_sync_helper);
|
||||
#endif
|
||||
2503
sound/core/seq/seq_clientmgr.c
Normal file
2503
sound/core/seq/seq_clientmgr.c
Normal file
File diff suppressed because it is too large
Load Diff
104
sound/core/seq/seq_clientmgr.h
Normal file
104
sound/core/seq/seq_clientmgr.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* ALSA sequencer Client Manager
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_CLIENTMGR_H
|
||||
#define __SND_SEQ_CLIENTMGR_H
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "seq_fifo.h"
|
||||
#include "seq_ports.h"
|
||||
#include "seq_lock.h"
|
||||
|
||||
|
||||
/* client manager */
|
||||
|
||||
struct _snd_seq_user_client {
|
||||
struct file *file; /* file struct of client */
|
||||
/* ... */
|
||||
|
||||
/* fifo */
|
||||
fifo_t *fifo; /* queue for incoming events */
|
||||
int fifo_pool_size;
|
||||
};
|
||||
|
||||
struct _snd_seq_kernel_client {
|
||||
snd_card_t *card;
|
||||
/* pointer to client functions */
|
||||
void *private_data; /* private data for client */
|
||||
/* ... */
|
||||
};
|
||||
|
||||
|
||||
struct _snd_seq_client {
|
||||
snd_seq_client_type_t type;
|
||||
unsigned int accept_input: 1,
|
||||
accept_output: 1;
|
||||
char name[64]; /* client name */
|
||||
int number; /* client number */
|
||||
unsigned int filter; /* filter flags */
|
||||
DECLARE_BITMAP(event_filter, 256);
|
||||
snd_use_lock_t use_lock;
|
||||
int event_lost;
|
||||
/* ports */
|
||||
int num_ports; /* number of ports */
|
||||
struct list_head ports_list_head;
|
||||
rwlock_t ports_lock;
|
||||
struct semaphore ports_mutex;
|
||||
int convert32; /* convert 32->64bit */
|
||||
|
||||
/* output pool */
|
||||
pool_t *pool; /* memory pool for this client */
|
||||
|
||||
union {
|
||||
user_client_t user;
|
||||
kernel_client_t kernel;
|
||||
} data;
|
||||
};
|
||||
|
||||
/* usage statistics */
|
||||
typedef struct {
|
||||
int cur;
|
||||
int peak;
|
||||
} usage_t;
|
||||
|
||||
|
||||
extern int client_init_data(void);
|
||||
extern int snd_sequencer_device_init(void);
|
||||
extern void snd_sequencer_device_done(void);
|
||||
|
||||
/* get locked pointer to client */
|
||||
extern client_t *snd_seq_client_use_ptr(int clientid);
|
||||
|
||||
/* unlock pointer to client */
|
||||
#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
|
||||
|
||||
/* dispatch event to client(s) */
|
||||
extern int snd_seq_dispatch_event(snd_seq_event_cell_t *cell, int atomic, int hop);
|
||||
|
||||
/* exported to other modules */
|
||||
extern int snd_seq_register_kernel_client(snd_seq_client_callback_t *callback, void *private_data);
|
||||
extern int snd_seq_unregister_kernel_client(int client);
|
||||
extern int snd_seq_kernel_client_enqueue(int client, snd_seq_event_t *ev, int atomic, int hop);
|
||||
int snd_seq_kernel_client_enqueue_blocking(int client, snd_seq_event_t * ev, struct file *file, int atomic, int hop);
|
||||
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
|
||||
int snd_seq_client_notify_subscription(int client, int port, snd_seq_port_subscribe_t *info, int evtype);
|
||||
|
||||
#endif
|
||||
137
sound/core/seq/seq_compat.c
Normal file
137
sound/core/seq/seq_compat.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 32bit -> 64bit ioctl wrapper for sequencer API
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file included from seq.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct sndrv_seq_port_info32 {
|
||||
struct sndrv_seq_addr addr; /* client/port numbers */
|
||||
char name[64]; /* port name */
|
||||
|
||||
u32 capability; /* port capability bits */
|
||||
u32 type; /* port type bits */
|
||||
s32 midi_channels; /* channels per MIDI port */
|
||||
s32 midi_voices; /* voices per MIDI port */
|
||||
s32 synth_voices; /* voices per SYNTH port */
|
||||
|
||||
s32 read_use; /* R/O: subscribers for output (from this port) */
|
||||
s32 write_use; /* R/O: subscribers for input (to this port) */
|
||||
|
||||
u32 kernel; /* reserved for kernel use (must be NULL) */
|
||||
u32 flags; /* misc. conditioning */
|
||||
unsigned char time_queue; /* queue # for timestamping */
|
||||
char reserved[59]; /* for future use */
|
||||
};
|
||||
|
||||
static int snd_seq_call_port_info_ioctl(client_t *client, unsigned int cmd,
|
||||
struct sndrv_seq_port_info32 __user *data32)
|
||||
{
|
||||
int err = -EFAULT;
|
||||
snd_seq_port_info_t *data;
|
||||
mm_segment_t fs;
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (! data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, data32, sizeof(*data32)) ||
|
||||
get_user(data->flags, &data32->flags) ||
|
||||
get_user(data->time_queue, &data32->time_queue))
|
||||
goto error;
|
||||
data->kernel = NULL;
|
||||
|
||||
fs = snd_enter_user();
|
||||
err = snd_seq_do_ioctl(client, cmd, data);
|
||||
snd_leave_user(fs);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (copy_to_user(data32, data, sizeof(*data32)) ||
|
||||
put_user(data->flags, &data32->flags) ||
|
||||
put_user(data->time_queue, &data32->time_queue))
|
||||
err = -EFAULT;
|
||||
|
||||
error:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
enum {
|
||||
SNDRV_SEQ_IOCTL_CREATE_PORT32 = _IOWR('S', 0x20, struct sndrv_seq_port_info32),
|
||||
SNDRV_SEQ_IOCTL_DELETE_PORT32 = _IOW ('S', 0x21, struct sndrv_seq_port_info32),
|
||||
SNDRV_SEQ_IOCTL_GET_PORT_INFO32 = _IOWR('S', 0x22, struct sndrv_seq_port_info32),
|
||||
SNDRV_SEQ_IOCTL_SET_PORT_INFO32 = _IOW ('S', 0x23, struct sndrv_seq_port_info32),
|
||||
SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32 = _IOWR('S', 0x52, struct sndrv_seq_port_info32),
|
||||
};
|
||||
|
||||
static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
client_t *client = (client_t *) file->private_data;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
|
||||
snd_assert(client != NULL, return -ENXIO);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_SEQ_IOCTL_PVERSION:
|
||||
case SNDRV_SEQ_IOCTL_CLIENT_ID:
|
||||
case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
|
||||
case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
|
||||
case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
|
||||
case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
|
||||
case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
|
||||
case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
|
||||
case SNDRV_SEQ_IOCTL_DELETE_QUEUE:
|
||||
case SNDRV_SEQ_IOCTL_GET_QUEUE_INFO:
|
||||
case SNDRV_SEQ_IOCTL_SET_QUEUE_INFO:
|
||||
case SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE:
|
||||
case SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS:
|
||||
case SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO:
|
||||
case SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO:
|
||||
case SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER:
|
||||
case SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER:
|
||||
case SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT:
|
||||
case SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT:
|
||||
case SNDRV_SEQ_IOCTL_GET_CLIENT_POOL:
|
||||
case SNDRV_SEQ_IOCTL_SET_CLIENT_POOL:
|
||||
case SNDRV_SEQ_IOCTL_REMOVE_EVENTS:
|
||||
case SNDRV_SEQ_IOCTL_QUERY_SUBS:
|
||||
case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
|
||||
case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
|
||||
case SNDRV_SEQ_IOCTL_RUNNING_MODE:
|
||||
return snd_seq_do_ioctl(client, cmd, argp);
|
||||
case SNDRV_SEQ_IOCTL_CREATE_PORT32:
|
||||
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
|
||||
case SNDRV_SEQ_IOCTL_DELETE_PORT32:
|
||||
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_DELETE_PORT, argp);
|
||||
case SNDRV_SEQ_IOCTL_GET_PORT_INFO32:
|
||||
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_GET_PORT_INFO, argp);
|
||||
case SNDRV_SEQ_IOCTL_SET_PORT_INFO32:
|
||||
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_SET_PORT_INFO, argp);
|
||||
case SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT32:
|
||||
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, argp);
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
575
sound/core/seq/seq_device.c
Normal file
575
sound/core/seq/seq_device.c
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* ALSA sequencer device management
|
||||
* Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*
|
||||
*----------------------------------------------------------------
|
||||
*
|
||||
* This device handler separates the card driver module from sequencer
|
||||
* stuff (sequencer core, synth drivers, etc), so that user can avoid
|
||||
* to spend unnecessary resources e.g. if he needs only listening to
|
||||
* MP3s.
|
||||
*
|
||||
* The card (or lowlevel) driver creates a sequencer device entry
|
||||
* via snd_seq_device_new(). This is an entry pointer to communicate
|
||||
* with the sequencer device "driver", which is involved with the
|
||||
* actual part to communicate with the sequencer core.
|
||||
* Each sequencer device entry has an id string and the corresponding
|
||||
* driver with the same id is loaded when required. For example,
|
||||
* lowlevel codes to access emu8000 chip on sbawe card are included in
|
||||
* emu8000-synth module. To activate this module, the hardware
|
||||
* resources like i/o port are passed via snd_seq_device argument.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/seq_device.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/initval.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("ALSA sequencer device management");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* driver list
|
||||
*/
|
||||
typedef struct ops_list ops_list_t;
|
||||
|
||||
/* driver state */
|
||||
#define DRIVER_EMPTY 0
|
||||
#define DRIVER_LOADED (1<<0)
|
||||
#define DRIVER_REQUESTED (1<<1)
|
||||
#define DRIVER_LOCKED (1<<2)
|
||||
|
||||
struct ops_list {
|
||||
char id[ID_LEN]; /* driver id */
|
||||
int driver; /* driver state */
|
||||
int used; /* reference counter */
|
||||
int argsize; /* argument size */
|
||||
|
||||
/* operators */
|
||||
snd_seq_dev_ops_t ops;
|
||||
|
||||
/* registred devices */
|
||||
struct list_head dev_list; /* list of devices */
|
||||
int num_devices; /* number of associated devices */
|
||||
int num_init_devices; /* number of initialized devices */
|
||||
struct semaphore reg_mutex;
|
||||
|
||||
struct list_head list; /* next driver */
|
||||
};
|
||||
|
||||
|
||||
static LIST_HEAD(opslist);
|
||||
static int num_ops;
|
||||
static DECLARE_MUTEX(ops_mutex);
|
||||
static snd_info_entry_t *info_entry = NULL;
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static int snd_seq_device_free(snd_seq_device_t *dev);
|
||||
static int snd_seq_device_dev_free(snd_device_t *device);
|
||||
static int snd_seq_device_dev_register(snd_device_t *device);
|
||||
static int snd_seq_device_dev_disconnect(snd_device_t *device);
|
||||
static int snd_seq_device_dev_unregister(snd_device_t *device);
|
||||
|
||||
static int init_device(snd_seq_device_t *dev, ops_list_t *ops);
|
||||
static int free_device(snd_seq_device_t *dev, ops_list_t *ops);
|
||||
static ops_list_t *find_driver(char *id, int create_if_empty);
|
||||
static ops_list_t *create_driver(char *id);
|
||||
static void unlock_driver(ops_list_t *ops);
|
||||
static void remove_drivers(void);
|
||||
|
||||
/*
|
||||
* show all drivers and their status
|
||||
*/
|
||||
|
||||
static void snd_seq_device_info(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
struct list_head *head;
|
||||
|
||||
down(&ops_mutex);
|
||||
list_for_each(head, &opslist) {
|
||||
ops_list_t *ops = list_entry(head, ops_list_t, list);
|
||||
snd_iprintf(buffer, "snd-%s%s%s%s,%d\n",
|
||||
ops->id,
|
||||
ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""),
|
||||
ops->driver & DRIVER_REQUESTED ? ",requested" : "",
|
||||
ops->driver & DRIVER_LOCKED ? ",locked" : "",
|
||||
ops->num_devices);
|
||||
}
|
||||
up(&ops_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* load all registered drivers (called from seq_clientmgr.c)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_KMOD
|
||||
/* avoid auto-loading during module_init() */
|
||||
static int snd_seq_in_init;
|
||||
void snd_seq_autoload_lock(void)
|
||||
{
|
||||
snd_seq_in_init++;
|
||||
}
|
||||
|
||||
void snd_seq_autoload_unlock(void)
|
||||
{
|
||||
snd_seq_in_init--;
|
||||
}
|
||||
#endif
|
||||
|
||||
void snd_seq_device_load_drivers(void)
|
||||
{
|
||||
#ifdef CONFIG_KMOD
|
||||
struct list_head *head;
|
||||
|
||||
/* Calling request_module during module_init()
|
||||
* may cause blocking.
|
||||
*/
|
||||
if (snd_seq_in_init)
|
||||
return;
|
||||
|
||||
if (! current->fs->root)
|
||||
return;
|
||||
|
||||
down(&ops_mutex);
|
||||
list_for_each(head, &opslist) {
|
||||
ops_list_t *ops = list_entry(head, ops_list_t, list);
|
||||
if (! (ops->driver & DRIVER_LOADED) &&
|
||||
! (ops->driver & DRIVER_REQUESTED)) {
|
||||
ops->used++;
|
||||
up(&ops_mutex);
|
||||
ops->driver |= DRIVER_REQUESTED;
|
||||
request_module("snd-%s", ops->id);
|
||||
down(&ops_mutex);
|
||||
ops->used--;
|
||||
}
|
||||
}
|
||||
up(&ops_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* register a sequencer device
|
||||
* card = card info (NULL allowed)
|
||||
* device = device number (if any)
|
||||
* id = id of driver
|
||||
* result = return pointer (NULL allowed if unnecessary)
|
||||
*/
|
||||
int snd_seq_device_new(snd_card_t *card, int device, char *id, int argsize,
|
||||
snd_seq_device_t **result)
|
||||
{
|
||||
snd_seq_device_t *dev;
|
||||
ops_list_t *ops;
|
||||
int err;
|
||||
static snd_device_ops_t dops = {
|
||||
.dev_free = snd_seq_device_dev_free,
|
||||
.dev_register = snd_seq_device_dev_register,
|
||||
.dev_disconnect = snd_seq_device_dev_disconnect,
|
||||
.dev_unregister = snd_seq_device_dev_unregister
|
||||
};
|
||||
|
||||
if (result)
|
||||
*result = NULL;
|
||||
|
||||
snd_assert(id != NULL, return -EINVAL);
|
||||
|
||||
ops = find_driver(id, 1);
|
||||
if (ops == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev = kcalloc(1, sizeof(*dev)*2 + argsize, GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
unlock_driver(ops);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* set up device info */
|
||||
dev->card = card;
|
||||
dev->device = device;
|
||||
strlcpy(dev->id, id, sizeof(dev->id));
|
||||
dev->argsize = argsize;
|
||||
dev->status = SNDRV_SEQ_DEVICE_FREE;
|
||||
|
||||
/* add this device to the list */
|
||||
down(&ops->reg_mutex);
|
||||
list_add_tail(&dev->list, &ops->dev_list);
|
||||
ops->num_devices++;
|
||||
up(&ops->reg_mutex);
|
||||
|
||||
unlock_driver(ops);
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
|
||||
snd_seq_device_free(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (result)
|
||||
*result = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free the existing device
|
||||
*/
|
||||
static int snd_seq_device_free(snd_seq_device_t *dev)
|
||||
{
|
||||
ops_list_t *ops;
|
||||
|
||||
snd_assert(dev != NULL, return -EINVAL);
|
||||
|
||||
ops = find_driver(dev->id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
/* remove the device from the list */
|
||||
down(&ops->reg_mutex);
|
||||
list_del(&dev->list);
|
||||
ops->num_devices--;
|
||||
up(&ops->reg_mutex);
|
||||
|
||||
free_device(dev, ops);
|
||||
if (dev->private_free)
|
||||
dev->private_free(dev);
|
||||
kfree(dev);
|
||||
|
||||
unlock_driver(ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_seq_device_dev_free(snd_device_t *device)
|
||||
{
|
||||
snd_seq_device_t *dev = device->device_data;
|
||||
return snd_seq_device_free(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* register the device
|
||||
*/
|
||||
static int snd_seq_device_dev_register(snd_device_t *device)
|
||||
{
|
||||
snd_seq_device_t *dev = device->device_data;
|
||||
ops_list_t *ops;
|
||||
|
||||
ops = find_driver(dev->id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
/* initialize this device if the corresponding driver was
|
||||
* already loaded
|
||||
*/
|
||||
if (ops->driver & DRIVER_LOADED)
|
||||
init_device(dev, ops);
|
||||
|
||||
unlock_driver(ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* disconnect the device
|
||||
*/
|
||||
static int snd_seq_device_dev_disconnect(snd_device_t *device)
|
||||
{
|
||||
snd_seq_device_t *dev = device->device_data;
|
||||
ops_list_t *ops;
|
||||
|
||||
ops = find_driver(dev->id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
free_device(dev, ops);
|
||||
|
||||
unlock_driver(ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* unregister the existing device
|
||||
*/
|
||||
static int snd_seq_device_dev_unregister(snd_device_t *device)
|
||||
{
|
||||
snd_seq_device_t *dev = device->device_data;
|
||||
return snd_seq_device_free(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* register device driver
|
||||
* id = driver id
|
||||
* entry = driver operators - duplicated to each instance
|
||||
*/
|
||||
int snd_seq_device_register_driver(char *id, snd_seq_dev_ops_t *entry, int argsize)
|
||||
{
|
||||
struct list_head *head;
|
||||
ops_list_t *ops;
|
||||
|
||||
if (id == NULL || entry == NULL ||
|
||||
entry->init_device == NULL || entry->free_device == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
snd_seq_autoload_lock();
|
||||
ops = find_driver(id, 1);
|
||||
if (ops == NULL) {
|
||||
snd_seq_autoload_unlock();
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ops->driver & DRIVER_LOADED) {
|
||||
snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
|
||||
unlock_driver(ops);
|
||||
snd_seq_autoload_unlock();
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
down(&ops->reg_mutex);
|
||||
/* copy driver operators */
|
||||
ops->ops = *entry;
|
||||
ops->driver |= DRIVER_LOADED;
|
||||
ops->argsize = argsize;
|
||||
|
||||
/* initialize existing devices if necessary */
|
||||
list_for_each(head, &ops->dev_list) {
|
||||
snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
|
||||
init_device(dev, ops);
|
||||
}
|
||||
up(&ops->reg_mutex);
|
||||
|
||||
unlock_driver(ops);
|
||||
snd_seq_autoload_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create driver record
|
||||
*/
|
||||
static ops_list_t * create_driver(char *id)
|
||||
{
|
||||
ops_list_t *ops;
|
||||
|
||||
ops = kmalloc(sizeof(*ops), GFP_KERNEL);
|
||||
if (ops == NULL)
|
||||
return ops;
|
||||
memset(ops, 0, sizeof(*ops));
|
||||
|
||||
/* set up driver entry */
|
||||
strlcpy(ops->id, id, sizeof(ops->id));
|
||||
init_MUTEX(&ops->reg_mutex);
|
||||
ops->driver = DRIVER_EMPTY;
|
||||
INIT_LIST_HEAD(&ops->dev_list);
|
||||
/* lock this instance */
|
||||
ops->used = 1;
|
||||
|
||||
/* register driver entry */
|
||||
down(&ops_mutex);
|
||||
list_add_tail(&ops->list, &opslist);
|
||||
num_ops++;
|
||||
up(&ops_mutex);
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* unregister the specified driver
|
||||
*/
|
||||
int snd_seq_device_unregister_driver(char *id)
|
||||
{
|
||||
struct list_head *head;
|
||||
ops_list_t *ops;
|
||||
|
||||
ops = find_driver(id, 0);
|
||||
if (ops == NULL)
|
||||
return -ENXIO;
|
||||
if (! (ops->driver & DRIVER_LOADED) ||
|
||||
(ops->driver & DRIVER_LOCKED)) {
|
||||
snd_printk(KERN_ERR "driver_unregister: cannot unload driver '%s': status=%x\n", id, ops->driver);
|
||||
unlock_driver(ops);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* close and release all devices associated with this driver */
|
||||
down(&ops->reg_mutex);
|
||||
ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */
|
||||
list_for_each(head, &ops->dev_list) {
|
||||
snd_seq_device_t *dev = list_entry(head, snd_seq_device_t, list);
|
||||
free_device(dev, ops);
|
||||
}
|
||||
|
||||
ops->driver = 0;
|
||||
if (ops->num_init_devices > 0)
|
||||
snd_printk(KERN_ERR "free_driver: init_devices > 0!! (%d)\n", ops->num_init_devices);
|
||||
up(&ops->reg_mutex);
|
||||
|
||||
unlock_driver(ops);
|
||||
|
||||
/* remove empty driver entries */
|
||||
remove_drivers();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove empty driver entries
|
||||
*/
|
||||
static void remove_drivers(void)
|
||||
{
|
||||
struct list_head *head;
|
||||
|
||||
down(&ops_mutex);
|
||||
head = opslist.next;
|
||||
while (head != &opslist) {
|
||||
ops_list_t *ops = list_entry(head, ops_list_t, list);
|
||||
if (! (ops->driver & DRIVER_LOADED) &&
|
||||
ops->used == 0 && ops->num_devices == 0) {
|
||||
head = head->next;
|
||||
list_del(&ops->list);
|
||||
kfree(ops);
|
||||
num_ops--;
|
||||
} else
|
||||
head = head->next;
|
||||
}
|
||||
up(&ops_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize the device - call init_device operator
|
||||
*/
|
||||
static int init_device(snd_seq_device_t *dev, ops_list_t *ops)
|
||||
{
|
||||
if (! (ops->driver & DRIVER_LOADED))
|
||||
return 0; /* driver is not loaded yet */
|
||||
if (dev->status != SNDRV_SEQ_DEVICE_FREE)
|
||||
return 0; /* already initialized */
|
||||
if (ops->argsize != dev->argsize) {
|
||||
snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ops->ops.init_device(dev) >= 0) {
|
||||
dev->status = SNDRV_SEQ_DEVICE_REGISTERED;
|
||||
ops->num_init_devices++;
|
||||
} else {
|
||||
snd_printk(KERN_ERR "init_device failed: %s: %s\n", dev->name, dev->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release the device - call free_device operator
|
||||
*/
|
||||
static int free_device(snd_seq_device_t *dev, ops_list_t *ops)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (! (ops->driver & DRIVER_LOADED))
|
||||
return 0; /* driver is not loaded yet */
|
||||
if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED)
|
||||
return 0; /* not registered */
|
||||
if (ops->argsize != dev->argsize) {
|
||||
snd_printk(KERN_ERR "incompatible device '%s' for plug-in '%s' (%d %d)\n", dev->name, ops->id, ops->argsize, dev->argsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) {
|
||||
dev->status = SNDRV_SEQ_DEVICE_FREE;
|
||||
dev->driver_data = NULL;
|
||||
ops->num_init_devices--;
|
||||
} else {
|
||||
snd_printk(KERN_ERR "free_device failed: %s: %s\n", dev->name, dev->id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* find the matching driver with given id
|
||||
*/
|
||||
static ops_list_t * find_driver(char *id, int create_if_empty)
|
||||
{
|
||||
struct list_head *head;
|
||||
|
||||
down(&ops_mutex);
|
||||
list_for_each(head, &opslist) {
|
||||
ops_list_t *ops = list_entry(head, ops_list_t, list);
|
||||
if (strcmp(ops->id, id) == 0) {
|
||||
ops->used++;
|
||||
up(&ops_mutex);
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
up(&ops_mutex);
|
||||
if (create_if_empty)
|
||||
return create_driver(id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void unlock_driver(ops_list_t *ops)
|
||||
{
|
||||
down(&ops_mutex);
|
||||
ops->used--;
|
||||
up(&ops_mutex);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* module part
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_device_init(void)
|
||||
{
|
||||
info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root);
|
||||
if (info_entry == NULL)
|
||||
return -ENOMEM;
|
||||
info_entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
info_entry->c.text.read_size = 2048;
|
||||
info_entry->c.text.read = snd_seq_device_info;
|
||||
if (snd_info_register(info_entry) < 0) {
|
||||
snd_info_free_entry(info_entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_device_exit(void)
|
||||
{
|
||||
remove_drivers();
|
||||
snd_info_unregister(info_entry);
|
||||
if (num_ops)
|
||||
snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
|
||||
}
|
||||
|
||||
module_init(alsa_seq_device_init)
|
||||
module_exit(alsa_seq_device_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_device_load_drivers);
|
||||
EXPORT_SYMBOL(snd_seq_device_new);
|
||||
EXPORT_SYMBOL(snd_seq_device_register_driver);
|
||||
EXPORT_SYMBOL(snd_seq_device_unregister_driver);
|
||||
#ifdef CONFIG_KMOD
|
||||
EXPORT_SYMBOL(snd_seq_autoload_lock);
|
||||
EXPORT_SYMBOL(snd_seq_autoload_unlock);
|
||||
#endif
|
||||
273
sound/core/seq/seq_dummy.c
Normal file
273
sound/core/seq/seq_dummy.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* ALSA sequencer MIDI-through client
|
||||
* Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include "seq_clientmgr.h"
|
||||
#include <sound/initval.h>
|
||||
#include <sound/asoundef.h>
|
||||
|
||||
/*
|
||||
|
||||
Sequencer MIDI-through client
|
||||
|
||||
This gives a simple midi-through client. All the normal input events
|
||||
are redirected to output port immediately.
|
||||
The routing can be done via aconnect program in alsa-utils.
|
||||
|
||||
Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
|
||||
If you want to auto-load this module, you may add the following alias
|
||||
in your /etc/conf.modules file.
|
||||
|
||||
alias snd-seq-client-62 snd-seq-dummy
|
||||
|
||||
The module is loaded on demand for client 62, or /proc/asound/seq/
|
||||
is accessed. If you don't need this module to be loaded, alias
|
||||
snd-seq-client-62 as "off". This will help modprobe.
|
||||
|
||||
The number of ports to be created can be specified via the module
|
||||
parameter "ports". For example, to create four ports, add the
|
||||
following option in /etc/modprobe.conf:
|
||||
|
||||
option snd-seq-dummy ports=4
|
||||
|
||||
The modle option "duplex=1" enables duplex operation to the port.
|
||||
In duplex mode, a pair of ports are created instead of single port,
|
||||
and events are tunneled between pair-ports. For example, input to
|
||||
port A is sent to output port of another port B and vice versa.
|
||||
In duplex mode, each port has DUPLEX capability.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
|
||||
|
||||
static int ports = 1;
|
||||
static int duplex = 0;
|
||||
|
||||
module_param(ports, int, 0444);
|
||||
MODULE_PARM_DESC(ports, "number of ports to be created");
|
||||
module_param(duplex, bool, 0444);
|
||||
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
|
||||
|
||||
typedef struct snd_seq_dummy_port {
|
||||
int client;
|
||||
int port;
|
||||
int duplex;
|
||||
int connect;
|
||||
} snd_seq_dummy_port_t;
|
||||
|
||||
static int my_client = -1;
|
||||
|
||||
/*
|
||||
* unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
|
||||
* to subscribers.
|
||||
* Note: this callback is called only after all subscribers are removed.
|
||||
*/
|
||||
static int
|
||||
dummy_unuse(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
snd_seq_dummy_port_t *p;
|
||||
int i;
|
||||
snd_seq_event_t ev;
|
||||
|
||||
p = private_data;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
if (p->duplex)
|
||||
ev.source.port = p->connect;
|
||||
else
|
||||
ev.source.port = p->port;
|
||||
ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
|
||||
for (i = 0; i < 16; i++) {
|
||||
ev.data.control.channel = i;
|
||||
ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
|
||||
snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
|
||||
ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
|
||||
snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* event input callback - just redirect events to subscribers
|
||||
*/
|
||||
static int
|
||||
dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
|
||||
{
|
||||
snd_seq_dummy_port_t *p;
|
||||
snd_seq_event_t tmpev;
|
||||
|
||||
p = private_data;
|
||||
if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
|
||||
ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
|
||||
return 0; /* ignore system messages */
|
||||
tmpev = *ev;
|
||||
if (p->duplex)
|
||||
tmpev.source.port = p->connect;
|
||||
else
|
||||
tmpev.source.port = p->port;
|
||||
tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
|
||||
}
|
||||
|
||||
/*
|
||||
* free_private callback
|
||||
*/
|
||||
static void
|
||||
dummy_free(void *private_data)
|
||||
{
|
||||
snd_seq_dummy_port_t *p;
|
||||
|
||||
p = private_data;
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* create a port
|
||||
*/
|
||||
static snd_seq_dummy_port_t __init *
|
||||
create_port(int idx, int type)
|
||||
{
|
||||
snd_seq_port_info_t pinfo;
|
||||
snd_seq_port_callback_t pcb;
|
||||
snd_seq_dummy_port_t *rec;
|
||||
|
||||
if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
|
||||
rec->client = my_client;
|
||||
rec->duplex = duplex;
|
||||
rec->connect = 0;
|
||||
memset(&pinfo, 0, sizeof(pinfo));
|
||||
pinfo.addr.client = my_client;
|
||||
if (duplex)
|
||||
sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
|
||||
(type ? 'B' : 'A'));
|
||||
else
|
||||
sprintf(pinfo.name, "Midi Through Port-%d", idx);
|
||||
pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
|
||||
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
|
||||
if (duplex)
|
||||
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
|
||||
pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
|
||||
memset(&pcb, 0, sizeof(pcb));
|
||||
pcb.owner = THIS_MODULE;
|
||||
pcb.unuse = dummy_unuse;
|
||||
pcb.event_input = dummy_input;
|
||||
pcb.private_free = dummy_free;
|
||||
pcb.private_data = rec;
|
||||
pinfo.kernel = &pcb;
|
||||
if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
|
||||
kfree(rec);
|
||||
return NULL;
|
||||
}
|
||||
rec->port = pinfo.addr.port;
|
||||
return rec;
|
||||
}
|
||||
|
||||
/*
|
||||
* register client and create ports
|
||||
*/
|
||||
static int __init
|
||||
register_client(void)
|
||||
{
|
||||
snd_seq_client_callback_t cb;
|
||||
snd_seq_client_info_t cinfo;
|
||||
snd_seq_dummy_port_t *rec1, *rec2;
|
||||
int i;
|
||||
|
||||
if (ports < 1) {
|
||||
snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* create client */
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.allow_input = 1;
|
||||
cb.allow_output = 1;
|
||||
my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY, &cb);
|
||||
if (my_client < 0)
|
||||
return my_client;
|
||||
|
||||
/* set client name */
|
||||
memset(&cinfo, 0, sizeof(cinfo));
|
||||
cinfo.client = my_client;
|
||||
cinfo.type = KERNEL_CLIENT;
|
||||
strcpy(cinfo.name, "Midi Through");
|
||||
snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
|
||||
|
||||
/* create ports */
|
||||
for (i = 0; i < ports; i++) {
|
||||
rec1 = create_port(i, 0);
|
||||
if (rec1 == NULL) {
|
||||
snd_seq_delete_kernel_client(my_client);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (duplex) {
|
||||
rec2 = create_port(i, 1);
|
||||
if (rec2 == NULL) {
|
||||
snd_seq_delete_kernel_client(my_client);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rec1->connect = rec2->port;
|
||||
rec2->connect = rec1->port;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete client if exists
|
||||
*/
|
||||
static void __exit
|
||||
delete_client(void)
|
||||
{
|
||||
if (my_client >= 0)
|
||||
snd_seq_delete_kernel_client(my_client);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_dummy_init(void)
|
||||
{
|
||||
int err;
|
||||
snd_seq_autoload_lock();
|
||||
err = register_client();
|
||||
snd_seq_autoload_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_dummy_exit(void)
|
||||
{
|
||||
delete_client();
|
||||
}
|
||||
|
||||
module_init(alsa_seq_dummy_init)
|
||||
module_exit(alsa_seq_dummy_exit)
|
||||
264
sound/core/seq/seq_fifo.c
Normal file
264
sound/core/seq/seq_fifo.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* ALSA sequencer FIFO
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include "seq_fifo.h"
|
||||
#include "seq_lock.h"
|
||||
|
||||
|
||||
/* FIFO */
|
||||
|
||||
/* create new fifo */
|
||||
fifo_t *snd_seq_fifo_new(int poolsize)
|
||||
{
|
||||
fifo_t *f;
|
||||
|
||||
f = kcalloc(1, sizeof(*f), GFP_KERNEL);
|
||||
if (f == NULL) {
|
||||
snd_printd("malloc failed for snd_seq_fifo_new() \n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->pool = snd_seq_pool_new(poolsize);
|
||||
if (f->pool == NULL) {
|
||||
kfree(f);
|
||||
return NULL;
|
||||
}
|
||||
if (snd_seq_pool_init(f->pool) < 0) {
|
||||
snd_seq_pool_delete(&f->pool);
|
||||
kfree(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_init(&f->lock);
|
||||
snd_use_lock_init(&f->use_lock);
|
||||
init_waitqueue_head(&f->input_sleep);
|
||||
atomic_set(&f->overflow, 0);
|
||||
|
||||
f->head = NULL;
|
||||
f->tail = NULL;
|
||||
f->cells = 0;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void snd_seq_fifo_delete(fifo_t **fifo)
|
||||
{
|
||||
fifo_t *f;
|
||||
|
||||
snd_assert(fifo != NULL, return);
|
||||
f = *fifo;
|
||||
snd_assert(f != NULL, return);
|
||||
*fifo = NULL;
|
||||
|
||||
snd_seq_fifo_clear(f);
|
||||
|
||||
/* wake up clients if any */
|
||||
if (waitqueue_active(&f->input_sleep))
|
||||
wake_up(&f->input_sleep);
|
||||
|
||||
/* release resources...*/
|
||||
/*....................*/
|
||||
|
||||
if (f->pool) {
|
||||
snd_seq_pool_done(f->pool);
|
||||
snd_seq_pool_delete(&f->pool);
|
||||
}
|
||||
|
||||
kfree(f);
|
||||
}
|
||||
|
||||
static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f);
|
||||
|
||||
/* clear queue */
|
||||
void snd_seq_fifo_clear(fifo_t *f)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
unsigned long flags;
|
||||
|
||||
/* clear overflow flag */
|
||||
atomic_set(&f->overflow, 0);
|
||||
|
||||
snd_use_lock_sync(&f->use_lock);
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
/* drain the fifo */
|
||||
while ((cell = fifo_cell_out(f)) != NULL) {
|
||||
snd_seq_cell_free(cell);
|
||||
}
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/* enqueue event to fifo */
|
||||
int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
snd_assert(f != NULL, return -EINVAL);
|
||||
|
||||
snd_use_lock_use(&f->use_lock);
|
||||
err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
|
||||
if (err < 0) {
|
||||
if (err == -ENOMEM)
|
||||
atomic_inc(&f->overflow);
|
||||
snd_use_lock_free(&f->use_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* append new cells to fifo */
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
if (f->tail != NULL)
|
||||
f->tail->next = cell;
|
||||
f->tail = cell;
|
||||
if (f->head == NULL)
|
||||
f->head = cell;
|
||||
f->cells++;
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
|
||||
/* wakeup client */
|
||||
if (waitqueue_active(&f->input_sleep))
|
||||
wake_up(&f->input_sleep);
|
||||
|
||||
snd_use_lock_free(&f->use_lock);
|
||||
|
||||
return 0; /* success */
|
||||
|
||||
}
|
||||
|
||||
/* dequeue cell from fifo */
|
||||
static snd_seq_event_cell_t *fifo_cell_out(fifo_t *f)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
|
||||
if ((cell = f->head) != NULL) {
|
||||
f->head = cell->next;
|
||||
|
||||
/* reset tail if this was the last element */
|
||||
if (f->tail == cell)
|
||||
f->tail = NULL;
|
||||
|
||||
cell->next = NULL;
|
||||
f->cells--;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* dequeue cell from fifo and copy on user space */
|
||||
int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
unsigned long flags;
|
||||
wait_queue_t wait;
|
||||
|
||||
snd_assert(f != NULL, return -EINVAL);
|
||||
|
||||
*cellp = NULL;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
while ((cell = fifo_cell_out(f)) == NULL) {
|
||||
if (nonblock) {
|
||||
/* non-blocking - return immediately */
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&f->input_sleep, &wait);
|
||||
spin_unlock_irq(&f->lock);
|
||||
schedule();
|
||||
spin_lock_irq(&f->lock);
|
||||
remove_wait_queue(&f->input_sleep, &wait);
|
||||
if (signal_pending(current)) {
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
*cellp = cell;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (cell) {
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
cell->next = f->head;
|
||||
f->head = cell;
|
||||
f->cells++;
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* polling; return non-zero if queue is available */
|
||||
int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait)
|
||||
{
|
||||
poll_wait(file, &f->input_sleep, wait);
|
||||
return (f->cells > 0);
|
||||
}
|
||||
|
||||
/* change the size of pool; all old events are removed */
|
||||
int snd_seq_fifo_resize(fifo_t *f, int poolsize)
|
||||
{
|
||||
unsigned long flags;
|
||||
pool_t *newpool, *oldpool;
|
||||
snd_seq_event_cell_t *cell, *next, *oldhead;
|
||||
|
||||
snd_assert(f != NULL && f->pool != NULL, return -EINVAL);
|
||||
|
||||
/* allocate new pool */
|
||||
newpool = snd_seq_pool_new(poolsize);
|
||||
if (newpool == NULL)
|
||||
return -ENOMEM;
|
||||
if (snd_seq_pool_init(newpool) < 0) {
|
||||
snd_seq_pool_delete(&newpool);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
/* remember old pool */
|
||||
oldpool = f->pool;
|
||||
oldhead = f->head;
|
||||
/* exchange pools */
|
||||
f->pool = newpool;
|
||||
f->head = NULL;
|
||||
f->tail = NULL;
|
||||
f->cells = 0;
|
||||
/* NOTE: overflow flag is not cleared */
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
|
||||
/* release cells in old pool */
|
||||
for (cell = oldhead; cell; cell = next) {
|
||||
next = cell->next;
|
||||
snd_seq_cell_free(cell);
|
||||
}
|
||||
snd_seq_pool_delete(&oldpool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
72
sound/core/seq/seq_fifo.h
Normal file
72
sound/core/seq/seq_fifo.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* ALSA sequencer FIFO
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_FIFO_H
|
||||
#define __SND_SEQ_FIFO_H
|
||||
|
||||
#include "seq_memory.h"
|
||||
#include "seq_lock.h"
|
||||
|
||||
|
||||
/* === FIFO === */
|
||||
|
||||
typedef struct {
|
||||
pool_t *pool; /* FIFO pool */
|
||||
snd_seq_event_cell_t* head; /* pointer to head of fifo */
|
||||
snd_seq_event_cell_t* tail; /* pointer to tail of fifo */
|
||||
int cells;
|
||||
spinlock_t lock;
|
||||
snd_use_lock_t use_lock;
|
||||
wait_queue_head_t input_sleep;
|
||||
atomic_t overflow;
|
||||
|
||||
} fifo_t;
|
||||
|
||||
/* create new fifo (constructor) */
|
||||
extern fifo_t *snd_seq_fifo_new(int poolsize);
|
||||
|
||||
/* delete fifo (destructor) */
|
||||
extern void snd_seq_fifo_delete(fifo_t **f);
|
||||
|
||||
|
||||
/* enqueue event to fifo */
|
||||
extern int snd_seq_fifo_event_in(fifo_t *f, snd_seq_event_t *event);
|
||||
|
||||
/* lock fifo from release */
|
||||
#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock)
|
||||
#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock)
|
||||
|
||||
/* get a cell from fifo - fifo should be locked */
|
||||
int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock);
|
||||
|
||||
/* free dequeued cell - fifo should be locked */
|
||||
extern void snd_seq_fifo_cell_putback(fifo_t *f, snd_seq_event_cell_t *cell);
|
||||
|
||||
/* clean up queue */
|
||||
extern void snd_seq_fifo_clear(fifo_t *f);
|
||||
|
||||
/* polling */
|
||||
extern int snd_seq_fifo_poll_wait(fifo_t *f, struct file *file, poll_table *wait);
|
||||
|
||||
/* resize pool in fifo */
|
||||
int snd_seq_fifo_resize(fifo_t *f, int poolsize);
|
||||
|
||||
|
||||
#endif
|
||||
75
sound/core/seq/seq_info.c
Normal file
75
sound/core/seq/seq_info.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* ALSA sequencer /proc interface
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "seq_info.h"
|
||||
#include "seq_clientmgr.h"
|
||||
#include "seq_timer.h"
|
||||
|
||||
|
||||
static snd_info_entry_t *queues_entry;
|
||||
static snd_info_entry_t *clients_entry;
|
||||
static snd_info_entry_t *timer_entry;
|
||||
|
||||
|
||||
static snd_info_entry_t * __init
|
||||
create_info_entry(char *name, int size, void (*read)(snd_info_entry_t *, snd_info_buffer_t *))
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, name, snd_seq_root);
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->c.text.read_size = size;
|
||||
entry->c.text.read = read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
return NULL;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/* create all our /proc entries */
|
||||
int __init snd_seq_info_init(void)
|
||||
{
|
||||
queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES),
|
||||
snd_seq_info_queues_read);
|
||||
clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS),
|
||||
snd_seq_info_clients_read);
|
||||
timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_seq_info_done(void)
|
||||
{
|
||||
if (queues_entry)
|
||||
snd_info_unregister(queues_entry);
|
||||
if (clients_entry)
|
||||
snd_info_unregister(clients_entry);
|
||||
if (timer_entry)
|
||||
snd_info_unregister(timer_entry);
|
||||
return 0;
|
||||
}
|
||||
36
sound/core/seq/seq_info.h
Normal file
36
sound/core/seq/seq_info.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ALSA sequencer /proc info
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_INFO_H
|
||||
#define __SND_SEQ_INFO_H
|
||||
|
||||
#include <sound/info.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
|
||||
void snd_seq_info_clients_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
|
||||
void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
|
||||
void snd_seq_info_queues_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
|
||||
|
||||
|
||||
int snd_seq_info_init( void );
|
||||
int snd_seq_info_done( void );
|
||||
|
||||
|
||||
#endif
|
||||
653
sound/core/seq/seq_instr.c
Normal file
653
sound/core/seq/seq_instr.c
Normal file
@@ -0,0 +1,653 @@
|
||||
/*
|
||||
* Generic Instrument routines for ALSA sequencer
|
||||
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include "seq_clientmgr.h"
|
||||
#include <sound/seq_instr.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list)
|
||||
{
|
||||
if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
|
||||
spin_lock_irqsave(&list->ops_lock, list->ops_flags);
|
||||
} else {
|
||||
down(&list->ops_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_instr_unlock_ops(snd_seq_kinstr_list_t *list)
|
||||
{
|
||||
if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) {
|
||||
spin_unlock_irqrestore(&list->ops_lock, list->ops_flags);
|
||||
} else {
|
||||
up(&list->ops_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static snd_seq_kinstr_t *snd_seq_instr_new(int add_len, int atomic)
|
||||
{
|
||||
snd_seq_kinstr_t *instr;
|
||||
|
||||
instr = kcalloc(1, sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL);
|
||||
if (instr == NULL)
|
||||
return NULL;
|
||||
instr->add_len = add_len;
|
||||
return instr;
|
||||
}
|
||||
|
||||
static int snd_seq_instr_free(snd_seq_kinstr_t *instr, int atomic)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (instr == NULL)
|
||||
return -EINVAL;
|
||||
if (instr->ops && instr->ops->remove)
|
||||
result = instr->ops->remove(instr->ops->private_data, instr, 1);
|
||||
if (!result)
|
||||
kfree(instr);
|
||||
return result;
|
||||
}
|
||||
|
||||
snd_seq_kinstr_list_t *snd_seq_instr_list_new(void)
|
||||
{
|
||||
snd_seq_kinstr_list_t *list;
|
||||
|
||||
list = kcalloc(1, sizeof(snd_seq_kinstr_list_t), GFP_KERNEL);
|
||||
if (list == NULL)
|
||||
return NULL;
|
||||
spin_lock_init(&list->lock);
|
||||
spin_lock_init(&list->ops_lock);
|
||||
init_MUTEX(&list->ops_mutex);
|
||||
list->owner = -1;
|
||||
return list;
|
||||
}
|
||||
|
||||
void snd_seq_instr_list_free(snd_seq_kinstr_list_t **list_ptr)
|
||||
{
|
||||
snd_seq_kinstr_list_t *list;
|
||||
snd_seq_kinstr_t *instr;
|
||||
snd_seq_kcluster_t *cluster;
|
||||
int idx;
|
||||
unsigned long flags;
|
||||
|
||||
if (list_ptr == NULL)
|
||||
return;
|
||||
list = *list_ptr;
|
||||
*list_ptr = NULL;
|
||||
if (list == NULL)
|
||||
return;
|
||||
|
||||
for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
|
||||
while ((instr = list->hash[idx]) != NULL) {
|
||||
list->hash[idx] = instr->next;
|
||||
list->count--;
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
while (instr->use) {
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
if (snd_seq_instr_free(instr, 0)<0)
|
||||
snd_printk(KERN_WARNING "instrument free problem\n");
|
||||
}
|
||||
while ((cluster = list->chash[idx]) != NULL) {
|
||||
list->chash[idx] = cluster->next;
|
||||
list->ccount--;
|
||||
kfree(cluster);
|
||||
}
|
||||
}
|
||||
kfree(list);
|
||||
}
|
||||
|
||||
static int instr_free_compare(snd_seq_kinstr_t *instr,
|
||||
snd_seq_instr_header_t *ifree,
|
||||
unsigned int client)
|
||||
{
|
||||
switch (ifree->cmd) {
|
||||
case SNDRV_SEQ_INSTR_FREE_CMD_ALL:
|
||||
/* all, except private for other clients */
|
||||
if ((instr->instr.std & 0xff000000) == 0)
|
||||
return 0;
|
||||
if (((instr->instr.std >> 24) & 0xff) == client)
|
||||
return 0;
|
||||
return 1;
|
||||
case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE:
|
||||
/* all my private instruments */
|
||||
if ((instr->instr.std & 0xff000000) == 0)
|
||||
return 1;
|
||||
if (((instr->instr.std >> 24) & 0xff) == client)
|
||||
return 0;
|
||||
return 1;
|
||||
case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER:
|
||||
/* all my private instruments */
|
||||
if ((instr->instr.std & 0xff000000) == 0) {
|
||||
if (instr->instr.cluster == ifree->id.cluster)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
if (((instr->instr.std >> 24) & 0xff) == client) {
|
||||
if (instr->instr.cluster == ifree->id.cluster)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int snd_seq_instr_list_free_cond(snd_seq_kinstr_list_t *list,
|
||||
snd_seq_instr_header_t *ifree,
|
||||
int client,
|
||||
int atomic)
|
||||
{
|
||||
snd_seq_kinstr_t *instr, *prev, *next, *flist;
|
||||
int idx;
|
||||
unsigned long flags;
|
||||
|
||||
snd_instr_lock_ops(list);
|
||||
for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) {
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
instr = list->hash[idx];
|
||||
prev = flist = NULL;
|
||||
while (instr) {
|
||||
while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) {
|
||||
prev = instr;
|
||||
instr = instr->next;
|
||||
}
|
||||
if (instr == NULL)
|
||||
continue;
|
||||
if (instr->ops && instr->ops->notify)
|
||||
instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
|
||||
next = instr->next;
|
||||
if (prev == NULL) {
|
||||
list->hash[idx] = next;
|
||||
} else {
|
||||
prev->next = next;
|
||||
}
|
||||
list->count--;
|
||||
instr->next = flist;
|
||||
flist = instr;
|
||||
instr = next;
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
while (flist) {
|
||||
instr = flist;
|
||||
flist = instr->next;
|
||||
while (instr->use) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
}
|
||||
if (snd_seq_instr_free(instr, atomic)<0)
|
||||
snd_printk(KERN_WARNING "instrument free problem\n");
|
||||
instr = next;
|
||||
}
|
||||
}
|
||||
snd_instr_unlock_ops(list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compute_hash_instr_key(snd_seq_instr_t *instr)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = instr->bank | (instr->prg << 16);
|
||||
result += result >> 24;
|
||||
result += result >> 16;
|
||||
result += result >> 8;
|
||||
return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = cluster;
|
||||
result += result >> 24;
|
||||
result += result >> 16;
|
||||
result += result >> 8;
|
||||
return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int compare_instr(snd_seq_instr_t *i1, snd_seq_instr_t *i2, int exact)
|
||||
{
|
||||
if (exact) {
|
||||
if (i1->cluster != i2->cluster ||
|
||||
i1->bank != i2->bank ||
|
||||
i1->prg != i2->prg)
|
||||
return 1;
|
||||
if ((i1->std & 0xff000000) != (i2->std & 0xff000000))
|
||||
return 1;
|
||||
if (!(i1->std & i2->std))
|
||||
return 1;
|
||||
return 0;
|
||||
} else {
|
||||
unsigned int client_check;
|
||||
|
||||
if (i2->cluster && i1->cluster != i2->cluster)
|
||||
return 1;
|
||||
client_check = i2->std & 0xff000000;
|
||||
if (client_check) {
|
||||
if ((i1->std & 0xff000000) != client_check)
|
||||
return 1;
|
||||
} else {
|
||||
if ((i1->std & i2->std) != i2->std)
|
||||
return 1;
|
||||
}
|
||||
return i1->bank != i2->bank || i1->prg != i2->prg;
|
||||
}
|
||||
}
|
||||
|
||||
snd_seq_kinstr_t *snd_seq_instr_find(snd_seq_kinstr_list_t *list,
|
||||
snd_seq_instr_t *instr,
|
||||
int exact,
|
||||
int follow_alias)
|
||||
{
|
||||
unsigned long flags;
|
||||
int depth = 0;
|
||||
snd_seq_kinstr_t *result;
|
||||
|
||||
if (list == NULL || instr == NULL)
|
||||
return NULL;
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
__again:
|
||||
result = list->hash[compute_hash_instr_key(instr)];
|
||||
while (result) {
|
||||
if (!compare_instr(&result->instr, instr, exact)) {
|
||||
if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) {
|
||||
instr = (snd_seq_instr_t *)KINSTR_DATA(result);
|
||||
if (++depth > 10)
|
||||
goto __not_found;
|
||||
goto __again;
|
||||
}
|
||||
result->use++;
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return result;
|
||||
}
|
||||
result = result->next;
|
||||
}
|
||||
__not_found:
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void snd_seq_instr_free_use(snd_seq_kinstr_list_t *list,
|
||||
snd_seq_kinstr_t *instr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (list == NULL || instr == NULL)
|
||||
return;
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
if (instr->use <= 0) {
|
||||
snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name);
|
||||
} else {
|
||||
instr->use--;
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
|
||||
static snd_seq_kinstr_ops_t *instr_ops(snd_seq_kinstr_ops_t *ops, char *instr_type)
|
||||
{
|
||||
while (ops) {
|
||||
if (!strcmp(ops->instr_type, instr_type))
|
||||
return ops;
|
||||
ops = ops->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int instr_result(snd_seq_event_t *ev,
|
||||
int type, int result,
|
||||
int atomic)
|
||||
{
|
||||
snd_seq_event_t sev;
|
||||
|
||||
memset(&sev, 0, sizeof(sev));
|
||||
sev.type = SNDRV_SEQ_EVENT_RESULT;
|
||||
sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED |
|
||||
SNDRV_SEQ_PRIORITY_NORMAL;
|
||||
sev.source = ev->dest;
|
||||
sev.dest = ev->source;
|
||||
sev.data.result.event = type;
|
||||
sev.data.result.result = result;
|
||||
#if 0
|
||||
printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n",
|
||||
type, result,
|
||||
sev.queue,
|
||||
sev.source.client, sev.source.port,
|
||||
sev.dest.client, sev.dest.port);
|
||||
#endif
|
||||
return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0);
|
||||
}
|
||||
|
||||
static int instr_begin(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
if (list->owner >= 0 && list->owner != ev->source.client) {
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic);
|
||||
}
|
||||
list->owner = ev->source.client;
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic);
|
||||
}
|
||||
|
||||
static int instr_end(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* TODO: timeout handling */
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
if (list->owner == ev->source.client) {
|
||||
list->owner = -1;
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic);
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic);
|
||||
}
|
||||
|
||||
static int instr_info(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_format_info(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_reset(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_status(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_put(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
unsigned long flags;
|
||||
snd_seq_instr_header_t put;
|
||||
snd_seq_kinstr_t *instr;
|
||||
int result = -EINVAL, len, key;
|
||||
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
|
||||
goto __return;
|
||||
|
||||
if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
|
||||
goto __return;
|
||||
if (copy_from_user(&put, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
|
||||
result = -EFAULT;
|
||||
goto __return;
|
||||
}
|
||||
snd_instr_lock_ops(list);
|
||||
if (put.id.instr.std & 0xff000000) { /* private instrument */
|
||||
put.id.instr.std &= 0x00ffffff;
|
||||
put.id.instr.std |= (unsigned int)ev->source.client << 24;
|
||||
}
|
||||
if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) {
|
||||
snd_seq_instr_free_use(list, instr);
|
||||
snd_instr_unlock_ops(list);
|
||||
result = -EBUSY;
|
||||
goto __return;
|
||||
}
|
||||
ops = instr_ops(ops, put.data.data.format);
|
||||
if (ops == NULL) {
|
||||
snd_instr_unlock_ops(list);
|
||||
goto __return;
|
||||
}
|
||||
len = ops->add_len;
|
||||
if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)
|
||||
len = sizeof(snd_seq_instr_t);
|
||||
instr = snd_seq_instr_new(len, atomic);
|
||||
if (instr == NULL) {
|
||||
snd_instr_unlock_ops(list);
|
||||
result = -ENOMEM;
|
||||
goto __return;
|
||||
}
|
||||
instr->ops = ops;
|
||||
instr->instr = put.id.instr;
|
||||
strlcpy(instr->name, put.data.name, sizeof(instr->name));
|
||||
instr->type = put.data.type;
|
||||
if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) {
|
||||
result = ops->put(ops->private_data,
|
||||
instr,
|
||||
(void __user *)ev->data.ext.ptr + sizeof(snd_seq_instr_header_t),
|
||||
ev->data.ext.len - sizeof(snd_seq_instr_header_t),
|
||||
atomic,
|
||||
put.cmd);
|
||||
if (result < 0) {
|
||||
snd_seq_instr_free(instr, atomic);
|
||||
snd_instr_unlock_ops(list);
|
||||
goto __return;
|
||||
}
|
||||
}
|
||||
key = compute_hash_instr_key(&instr->instr);
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
instr->next = list->hash[key];
|
||||
list->hash[key] = instr;
|
||||
list->count++;
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
snd_instr_unlock_ops(list);
|
||||
result = 0;
|
||||
__return:
|
||||
instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int instr_get(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_free(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
snd_seq_instr_header_t ifree;
|
||||
snd_seq_kinstr_t *instr, *prev;
|
||||
int result = -EINVAL;
|
||||
unsigned long flags;
|
||||
unsigned int hash;
|
||||
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR)
|
||||
goto __return;
|
||||
|
||||
if (ev->data.ext.len < sizeof(snd_seq_instr_header_t))
|
||||
goto __return;
|
||||
if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr, sizeof(snd_seq_instr_header_t))) {
|
||||
result = -EFAULT;
|
||||
goto __return;
|
||||
}
|
||||
if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL ||
|
||||
ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE ||
|
||||
ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) {
|
||||
result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic);
|
||||
goto __return;
|
||||
}
|
||||
if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) {
|
||||
if (ifree.id.instr.std & 0xff000000) {
|
||||
ifree.id.instr.std &= 0x00ffffff;
|
||||
ifree.id.instr.std |= (unsigned int)ev->source.client << 24;
|
||||
}
|
||||
hash = compute_hash_instr_key(&ifree.id.instr);
|
||||
snd_instr_lock_ops(list);
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
instr = list->hash[hash];
|
||||
prev = NULL;
|
||||
while (instr) {
|
||||
if (!compare_instr(&instr->instr, &ifree.id.instr, 1))
|
||||
goto __free_single;
|
||||
prev = instr;
|
||||
instr = instr->next;
|
||||
}
|
||||
result = -ENOENT;
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
snd_instr_unlock_ops(list);
|
||||
goto __return;
|
||||
|
||||
__free_single:
|
||||
if (prev) {
|
||||
prev->next = instr->next;
|
||||
} else {
|
||||
list->hash[hash] = instr->next;
|
||||
}
|
||||
if (instr->ops && instr->ops->notify)
|
||||
instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
|
||||
while (instr->use) {
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
result = snd_seq_instr_free(instr, atomic);
|
||||
snd_instr_unlock_ops(list);
|
||||
goto __return;
|
||||
}
|
||||
|
||||
__return:
|
||||
instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int instr_list(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int instr_cluster(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int atomic, int hop)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
int snd_seq_instr_event(snd_seq_kinstr_ops_t *ops,
|
||||
snd_seq_kinstr_list_t *list,
|
||||
snd_seq_event_t *ev,
|
||||
int client,
|
||||
int atomic,
|
||||
int hop)
|
||||
{
|
||||
int direct = 0;
|
||||
|
||||
snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL);
|
||||
if (snd_seq_ev_is_direct(ev)) {
|
||||
direct = 1;
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_INSTR_BEGIN:
|
||||
return instr_begin(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_END:
|
||||
return instr_end(ops, list, ev, atomic, hop);
|
||||
}
|
||||
}
|
||||
if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct)
|
||||
return -EINVAL;
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_INSTR_INFO:
|
||||
return instr_info(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_FINFO:
|
||||
return instr_format_info(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_RESET:
|
||||
return instr_reset(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_STATUS:
|
||||
return instr_status(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_PUT:
|
||||
return instr_put(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_GET:
|
||||
return instr_get(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_FREE:
|
||||
return instr_free(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_LIST:
|
||||
return instr_list(ops, list, ev, atomic, hop);
|
||||
case SNDRV_SEQ_EVENT_INSTR_CLUSTER:
|
||||
return instr_cluster(ops, list, ev, atomic, hop);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init part
|
||||
*/
|
||||
|
||||
static int __init alsa_seq_instr_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_instr_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_seq_instr_init)
|
||||
module_exit(alsa_seq_instr_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_seq_instr_list_new);
|
||||
EXPORT_SYMBOL(snd_seq_instr_list_free);
|
||||
EXPORT_SYMBOL(snd_seq_instr_list_free_cond);
|
||||
EXPORT_SYMBOL(snd_seq_instr_find);
|
||||
EXPORT_SYMBOL(snd_seq_instr_free_use);
|
||||
EXPORT_SYMBOL(snd_seq_instr_event);
|
||||
48
sound/core/seq/seq_lock.c
Normal file
48
sound/core/seq/seq_lock.c
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Do sleep inside a spin-lock
|
||||
* Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include "seq_lock.h"
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
|
||||
|
||||
/* wait until all locks are released */
|
||||
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
|
||||
{
|
||||
int max_count = 5 * HZ;
|
||||
|
||||
if (atomic_read(lockp) < 0) {
|
||||
printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
|
||||
return;
|
||||
}
|
||||
while (atomic_read(lockp) > 0) {
|
||||
if (max_count == 0) {
|
||||
snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
|
||||
break;
|
||||
}
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
max_count--;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
33
sound/core/seq/seq_lock.h
Normal file
33
sound/core/seq/seq_lock.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef __SND_SEQ_LOCK_H
|
||||
#define __SND_SEQ_LOCK_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
|
||||
|
||||
typedef atomic_t snd_use_lock_t;
|
||||
|
||||
/* initialize lock */
|
||||
#define snd_use_lock_init(lockp) atomic_set(lockp, 0)
|
||||
|
||||
/* increment lock */
|
||||
#define snd_use_lock_use(lockp) atomic_inc(lockp)
|
||||
|
||||
/* release lock */
|
||||
#define snd_use_lock_free(lockp) atomic_dec(lockp)
|
||||
|
||||
/* wait until all locks are released */
|
||||
void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
|
||||
#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
|
||||
|
||||
#else /* SMP || CONFIG_SND_DEBUG */
|
||||
|
||||
typedef spinlock_t snd_use_lock_t; /* dummy */
|
||||
#define snd_use_lock_init(lockp) /**/
|
||||
#define snd_use_lock_use(lockp) /**/
|
||||
#define snd_use_lock_free(lockp) /**/
|
||||
#define snd_use_lock_sync(lockp) /**/
|
||||
|
||||
#endif /* SMP || CONFIG_SND_DEBUG */
|
||||
|
||||
#endif /* __SND_SEQ_LOCK_H */
|
||||
510
sound/core/seq/seq_memory.c
Normal file
510
sound/core/seq/seq_memory.c
Normal file
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* ALSA sequencer Memory Manager
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
* 2000 by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
#include "seq_memory.h"
|
||||
#include "seq_queue.h"
|
||||
#include "seq_info.h"
|
||||
#include "seq_lock.h"
|
||||
|
||||
/* semaphore in struct file record */
|
||||
#define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem)
|
||||
|
||||
|
||||
inline static int snd_seq_pool_available(pool_t *pool)
|
||||
{
|
||||
return pool->total_elements - atomic_read(&pool->counter);
|
||||
}
|
||||
|
||||
inline static int snd_seq_output_ok(pool_t *pool)
|
||||
{
|
||||
return snd_seq_pool_available(pool) >= pool->room;
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable length event:
|
||||
* The event like sysex uses variable length type.
|
||||
* The external data may be stored in three different formats.
|
||||
* 1) kernel space
|
||||
* This is the normal case.
|
||||
* ext.data.len = length
|
||||
* ext.data.ptr = buffer pointer
|
||||
* 2) user space
|
||||
* When an event is generated via read(), the external data is
|
||||
* kept in user space until expanded.
|
||||
* ext.data.len = length | SNDRV_SEQ_EXT_USRPTR
|
||||
* ext.data.ptr = userspace pointer
|
||||
* 3) chained cells
|
||||
* When the variable length event is enqueued (in prioq or fifo),
|
||||
* the external data is decomposed to several cells.
|
||||
* ext.data.len = length | SNDRV_SEQ_EXT_CHAINED
|
||||
* ext.data.ptr = the additiona cell head
|
||||
* -> cell.next -> cell.next -> ..
|
||||
*/
|
||||
|
||||
/*
|
||||
* exported:
|
||||
* call dump function to expand external data.
|
||||
*/
|
||||
|
||||
static int get_var_len(const snd_seq_event_t *event)
|
||||
{
|
||||
if ((event->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
|
||||
return -EINVAL;
|
||||
|
||||
return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
||||
}
|
||||
|
||||
int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data)
|
||||
{
|
||||
int len, err;
|
||||
snd_seq_event_cell_t *cell;
|
||||
|
||||
if ((len = get_var_len(event)) <= 0)
|
||||
return len;
|
||||
|
||||
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
||||
char buf[32];
|
||||
char __user *curptr = (char __user *)event->data.ext.ptr;
|
||||
while (len > 0) {
|
||||
int size = sizeof(buf);
|
||||
if (len < size)
|
||||
size = len;
|
||||
if (copy_from_user(buf, curptr, size))
|
||||
return -EFAULT;
|
||||
err = func(private_data, buf, size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
curptr += size;
|
||||
len -= size;
|
||||
}
|
||||
return 0;
|
||||
} if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
|
||||
return func(private_data, event->data.ext.ptr, len);
|
||||
}
|
||||
|
||||
cell = (snd_seq_event_cell_t*)event->data.ext.ptr;
|
||||
for (; len > 0 && cell; cell = cell->next) {
|
||||
int size = sizeof(snd_seq_event_t);
|
||||
if (len < size)
|
||||
size = len;
|
||||
err = func(private_data, &cell->event, size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
len -= size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* exported:
|
||||
* expand the variable length event to linear buffer space.
|
||||
*/
|
||||
|
||||
static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
|
||||
{
|
||||
memcpy(*bufptr, src, size);
|
||||
*bufptr += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
|
||||
{
|
||||
if (copy_to_user(*bufptr, src, size))
|
||||
return -EFAULT;
|
||||
*bufptr += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned)
|
||||
{
|
||||
int len, newlen;
|
||||
int err;
|
||||
|
||||
if ((len = get_var_len(event)) < 0)
|
||||
return len;
|
||||
newlen = len;
|
||||
if (size_aligned > 0)
|
||||
newlen = ((len + size_aligned - 1) / size_aligned) * size_aligned;
|
||||
if (count < newlen)
|
||||
return -EAGAIN;
|
||||
|
||||
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
||||
if (! in_kernel)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
|
||||
return -EFAULT;
|
||||
return newlen;
|
||||
}
|
||||
err = snd_seq_dump_var_event(event,
|
||||
in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
|
||||
(snd_seq_dump_func_t)seq_copy_in_user,
|
||||
&buf);
|
||||
return err < 0 ? err : newlen;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release this cell, free extended data if available
|
||||
*/
|
||||
|
||||
static inline void free_cell(pool_t *pool, snd_seq_event_cell_t *cell)
|
||||
{
|
||||
cell->next = pool->free;
|
||||
pool->free = cell;
|
||||
atomic_dec(&pool->counter);
|
||||
}
|
||||
|
||||
void snd_seq_cell_free(snd_seq_event_cell_t * cell)
|
||||
{
|
||||
unsigned long flags;
|
||||
pool_t *pool;
|
||||
|
||||
snd_assert(cell != NULL, return);
|
||||
pool = cell->pool;
|
||||
snd_assert(pool != NULL, return);
|
||||
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
free_cell(pool, cell);
|
||||
if (snd_seq_ev_is_variable(&cell->event)) {
|
||||
if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
|
||||
snd_seq_event_cell_t *curp, *nextptr;
|
||||
curp = cell->event.data.ext.ptr;
|
||||
for (; curp; curp = nextptr) {
|
||||
nextptr = curp->next;
|
||||
curp->next = pool->free;
|
||||
free_cell(pool, curp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (waitqueue_active(&pool->output_sleep)) {
|
||||
/* has enough space now? */
|
||||
if (snd_seq_output_ok(pool))
|
||||
wake_up(&pool->output_sleep);
|
||||
}
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* allocate an event cell.
|
||||
*/
|
||||
static int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
unsigned long flags;
|
||||
int err = -EAGAIN;
|
||||
wait_queue_t wait;
|
||||
|
||||
if (pool == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*cellp = NULL;
|
||||
|
||||
init_waitqueue_entry(&wait, current);
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
if (pool->ptr == NULL) { /* not initialized */
|
||||
snd_printd("seq: pool is not initialized\n");
|
||||
err = -EINVAL;
|
||||
goto __error;
|
||||
}
|
||||
while (pool->free == NULL && ! nonblock && ! pool->closing) {
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&pool->output_sleep, &wait);
|
||||
spin_unlock_irq(&pool->lock);
|
||||
schedule();
|
||||
spin_lock_irq(&pool->lock);
|
||||
remove_wait_queue(&pool->output_sleep, &wait);
|
||||
/* interrupted? */
|
||||
if (signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
if (pool->closing) { /* closing.. */
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
cell = pool->free;
|
||||
if (cell) {
|
||||
int used;
|
||||
pool->free = cell->next;
|
||||
atomic_inc(&pool->counter);
|
||||
used = atomic_read(&pool->counter);
|
||||
if (pool->max_used < used)
|
||||
pool->max_used = used;
|
||||
pool->event_alloc_success++;
|
||||
/* clear cell pointers */
|
||||
cell->next = NULL;
|
||||
err = 0;
|
||||
} else
|
||||
pool->event_alloc_failures++;
|
||||
*cellp = cell;
|
||||
|
||||
__error:
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* duplicate the event to a cell.
|
||||
* if the event has external data, the data is decomposed to additional
|
||||
* cells.
|
||||
*/
|
||||
int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file)
|
||||
{
|
||||
int ncells, err;
|
||||
unsigned int extlen;
|
||||
snd_seq_event_cell_t *cell;
|
||||
|
||||
*cellp = NULL;
|
||||
|
||||
ncells = 0;
|
||||
extlen = 0;
|
||||
if (snd_seq_ev_is_variable(event)) {
|
||||
extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
||||
ncells = (extlen + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t);
|
||||
}
|
||||
if (ncells >= pool->total_elements)
|
||||
return -ENOMEM;
|
||||
|
||||
err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* copy the event */
|
||||
cell->event = *event;
|
||||
|
||||
/* decompose */
|
||||
if (snd_seq_ev_is_variable(event)) {
|
||||
int len = extlen;
|
||||
int is_chained = event->data.ext.len & SNDRV_SEQ_EXT_CHAINED;
|
||||
int is_usrptr = event->data.ext.len & SNDRV_SEQ_EXT_USRPTR;
|
||||
snd_seq_event_cell_t *src, *tmp, *tail;
|
||||
char *buf;
|
||||
|
||||
cell->event.data.ext.len = extlen | SNDRV_SEQ_EXT_CHAINED;
|
||||
cell->event.data.ext.ptr = NULL;
|
||||
|
||||
src = (snd_seq_event_cell_t*)event->data.ext.ptr;
|
||||
buf = (char *)event->data.ext.ptr;
|
||||
tail = NULL;
|
||||
|
||||
while (ncells-- > 0) {
|
||||
int size = sizeof(snd_seq_event_t);
|
||||
if (len < size)
|
||||
size = len;
|
||||
err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
|
||||
if (err < 0)
|
||||
goto __error;
|
||||
if (cell->event.data.ext.ptr == NULL)
|
||||
cell->event.data.ext.ptr = tmp;
|
||||
if (tail)
|
||||
tail->next = tmp;
|
||||
tail = tmp;
|
||||
/* copy chunk */
|
||||
if (is_chained && src) {
|
||||
tmp->event = src->event;
|
||||
src = src->next;
|
||||
} else if (is_usrptr) {
|
||||
if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
|
||||
err = -EFAULT;
|
||||
goto __error;
|
||||
}
|
||||
} else {
|
||||
memcpy(&tmp->event, buf, size);
|
||||
}
|
||||
buf += size;
|
||||
len -= size;
|
||||
}
|
||||
}
|
||||
|
||||
*cellp = cell;
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
snd_seq_cell_free(cell);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* poll wait */
|
||||
int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait)
|
||||
{
|
||||
poll_wait(file, &pool->output_sleep, wait);
|
||||
return snd_seq_output_ok(pool);
|
||||
}
|
||||
|
||||
|
||||
/* allocate room specified number of events */
|
||||
int snd_seq_pool_init(pool_t *pool)
|
||||
{
|
||||
int cell;
|
||||
snd_seq_event_cell_t *cellptr;
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(pool != NULL, return -EINVAL);
|
||||
if (pool->ptr) /* should be atomic? */
|
||||
return 0;
|
||||
|
||||
pool->ptr = vmalloc(sizeof(snd_seq_event_cell_t) * pool->size);
|
||||
if (pool->ptr == NULL) {
|
||||
snd_printd("seq: malloc for sequencer events failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* add new cells to the free cell list */
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
pool->free = NULL;
|
||||
|
||||
for (cell = 0; cell < pool->size; cell++) {
|
||||
cellptr = pool->ptr + cell;
|
||||
cellptr->pool = pool;
|
||||
cellptr->next = pool->free;
|
||||
pool->free = cellptr;
|
||||
}
|
||||
pool->room = (pool->size + 1) / 2;
|
||||
|
||||
/* init statistics */
|
||||
pool->max_used = 0;
|
||||
pool->total_elements = pool->size;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove events */
|
||||
int snd_seq_pool_done(pool_t *pool)
|
||||
{
|
||||
unsigned long flags;
|
||||
snd_seq_event_cell_t *ptr;
|
||||
int max_count = 5 * HZ;
|
||||
|
||||
snd_assert(pool != NULL, return -EINVAL);
|
||||
|
||||
/* wait for closing all threads */
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
pool->closing = 1;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
|
||||
if (waitqueue_active(&pool->output_sleep))
|
||||
wake_up(&pool->output_sleep);
|
||||
|
||||
while (atomic_read(&pool->counter) > 0) {
|
||||
if (max_count == 0) {
|
||||
snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
|
||||
break;
|
||||
}
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
max_count--;
|
||||
}
|
||||
|
||||
/* release all resources */
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
ptr = pool->ptr;
|
||||
pool->ptr = NULL;
|
||||
pool->free = NULL;
|
||||
pool->total_elements = 0;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
|
||||
vfree(ptr);
|
||||
|
||||
spin_lock_irqsave(&pool->lock, flags);
|
||||
pool->closing = 0;
|
||||
spin_unlock_irqrestore(&pool->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* init new memory pool */
|
||||
pool_t *snd_seq_pool_new(int poolsize)
|
||||
{
|
||||
pool_t *pool;
|
||||
|
||||
/* create pool block */
|
||||
pool = kcalloc(1, sizeof(*pool), GFP_KERNEL);
|
||||
if (pool == NULL) {
|
||||
snd_printd("seq: malloc failed for pool\n");
|
||||
return NULL;
|
||||
}
|
||||
spin_lock_init(&pool->lock);
|
||||
pool->ptr = NULL;
|
||||
pool->free = NULL;
|
||||
pool->total_elements = 0;
|
||||
atomic_set(&pool->counter, 0);
|
||||
pool->closing = 0;
|
||||
init_waitqueue_head(&pool->output_sleep);
|
||||
|
||||
pool->size = poolsize;
|
||||
|
||||
/* init statistics */
|
||||
pool->max_used = 0;
|
||||
return pool;
|
||||
}
|
||||
|
||||
/* remove memory pool */
|
||||
int snd_seq_pool_delete(pool_t **ppool)
|
||||
{
|
||||
pool_t *pool = *ppool;
|
||||
|
||||
*ppool = NULL;
|
||||
if (pool == NULL)
|
||||
return 0;
|
||||
snd_seq_pool_done(pool);
|
||||
kfree(pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initialize sequencer memory */
|
||||
int __init snd_sequencer_memory_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* release sequencer memory */
|
||||
void __exit snd_sequencer_memory_done(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* exported to seq_clientmgr.c */
|
||||
void snd_seq_info_pool(snd_info_buffer_t * buffer, pool_t *pool, char *space)
|
||||
{
|
||||
if (pool == NULL)
|
||||
return;
|
||||
snd_iprintf(buffer, "%sPool size : %d\n", space, pool->total_elements);
|
||||
snd_iprintf(buffer, "%sCells in use : %d\n", space, atomic_read(&pool->counter));
|
||||
snd_iprintf(buffer, "%sPeak cells in use : %d\n", space, pool->max_used);
|
||||
snd_iprintf(buffer, "%sAlloc success : %d\n", space, pool->event_alloc_success);
|
||||
snd_iprintf(buffer, "%sAlloc failures : %d\n", space, pool->event_alloc_failures);
|
||||
}
|
||||
104
sound/core/seq/seq_memory.h
Normal file
104
sound/core/seq/seq_memory.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* ALSA sequencer Memory Manager
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_MEMORYMGR_H
|
||||
#define __SND_SEQ_MEMORYMGR_H
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
typedef struct pool pool_t;
|
||||
|
||||
/* container for sequencer event (internal use) */
|
||||
typedef struct snd_seq_event_cell_t {
|
||||
snd_seq_event_t event;
|
||||
pool_t *pool; /* used pool */
|
||||
struct snd_seq_event_cell_t *next; /* next cell */
|
||||
} snd_seq_event_cell_t;
|
||||
|
||||
/* design note: the pool is a contigious block of memory, if we dynamicly
|
||||
want to add additional cells to the pool be better store this in another
|
||||
pool as we need to know the base address of the pool when releasing
|
||||
memory. */
|
||||
|
||||
struct pool {
|
||||
snd_seq_event_cell_t *ptr; /* pointer to first event chunk */
|
||||
snd_seq_event_cell_t *free; /* pointer to the head of the free list */
|
||||
|
||||
int total_elements; /* pool size actually allocated */
|
||||
atomic_t counter; /* cells free */
|
||||
|
||||
int size; /* pool size to be allocated */
|
||||
int room; /* watermark for sleep/wakeup */
|
||||
|
||||
int closing;
|
||||
|
||||
/* statistics */
|
||||
int max_used;
|
||||
int event_alloc_nopool;
|
||||
int event_alloc_failures;
|
||||
int event_alloc_success;
|
||||
|
||||
/* Write locking */
|
||||
wait_queue_head_t output_sleep;
|
||||
|
||||
/* Pool lock */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
extern void snd_seq_cell_free(snd_seq_event_cell_t* cell);
|
||||
|
||||
int snd_seq_event_dup(pool_t *pool, snd_seq_event_t *event, snd_seq_event_cell_t **cellp, int nonblock, struct file *file);
|
||||
|
||||
/* return number of unused (free) cells */
|
||||
static inline int snd_seq_unused_cells(pool_t *pool)
|
||||
{
|
||||
return pool ? pool->total_elements - atomic_read(&pool->counter) : 0;
|
||||
}
|
||||
|
||||
/* return total number of allocated cells */
|
||||
static inline int snd_seq_total_cells(pool_t *pool)
|
||||
{
|
||||
return pool ? pool->total_elements : 0;
|
||||
}
|
||||
|
||||
/* init pool - allocate events */
|
||||
int snd_seq_pool_init(pool_t *pool);
|
||||
|
||||
/* done pool - free events */
|
||||
int snd_seq_pool_done(pool_t *pool);
|
||||
|
||||
/* create pool */
|
||||
pool_t *snd_seq_pool_new(int poolsize);
|
||||
|
||||
/* remove pool */
|
||||
int snd_seq_pool_delete(pool_t **pool);
|
||||
|
||||
/* init memory */
|
||||
int snd_sequencer_memory_init(void);
|
||||
|
||||
/* release event memory */
|
||||
void snd_sequencer_memory_done(void);
|
||||
|
||||
/* polling */
|
||||
int snd_seq_pool_poll_wait(pool_t *pool, struct file *file, poll_table *wait);
|
||||
|
||||
|
||||
#endif
|
||||
489
sound/core/seq/seq_midi.c
Normal file
489
sound/core/seq/seq_midi.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Generic MIDI synth driver for ALSA sequencer
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
Possible options for midisynth module:
|
||||
- automatic opening of midi ports on first received event or subscription
|
||||
(close will be performed when client leaves)
|
||||
*/
|
||||
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/seq_device.h>
|
||||
#include <sound/seq_midi_event.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
|
||||
MODULE_LICENSE("GPL");
|
||||
static int output_buffer_size = PAGE_SIZE;
|
||||
module_param(output_buffer_size, int, 0644);
|
||||
MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
|
||||
static int input_buffer_size = PAGE_SIZE;
|
||||
module_param(input_buffer_size, int, 0644);
|
||||
MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
|
||||
|
||||
/* data for this midi synth driver */
|
||||
typedef struct {
|
||||
snd_card_t *card;
|
||||
int device;
|
||||
int subdevice;
|
||||
snd_rawmidi_file_t input_rfile;
|
||||
snd_rawmidi_file_t output_rfile;
|
||||
int seq_client;
|
||||
int seq_port;
|
||||
snd_midi_event_t *parser;
|
||||
} seq_midisynth_t;
|
||||
|
||||
typedef struct {
|
||||
int seq_client;
|
||||
int num_ports;
|
||||
int ports_per_device[SNDRV_RAWMIDI_DEVICES];
|
||||
seq_midisynth_t *ports[SNDRV_RAWMIDI_DEVICES];
|
||||
} seq_midisynth_client_t;
|
||||
|
||||
static seq_midisynth_client_t *synths[SNDRV_CARDS];
|
||||
static DECLARE_MUTEX(register_mutex);
|
||||
|
||||
/* handle rawmidi input event (MIDI v1.0 stream) */
|
||||
static void snd_midi_input_event(snd_rawmidi_substream_t * substream)
|
||||
{
|
||||
snd_rawmidi_runtime_t *runtime;
|
||||
seq_midisynth_t *msynth;
|
||||
snd_seq_event_t ev;
|
||||
char buf[16], *pbuf;
|
||||
long res, count;
|
||||
|
||||
if (substream == NULL)
|
||||
return;
|
||||
runtime = substream->runtime;
|
||||
msynth = (seq_midisynth_t *) runtime->private_data;
|
||||
if (msynth == NULL)
|
||||
return;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
while (runtime->avail > 0) {
|
||||
res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
|
||||
if (res <= 0)
|
||||
continue;
|
||||
if (msynth->parser == NULL)
|
||||
continue;
|
||||
pbuf = buf;
|
||||
while (res > 0) {
|
||||
count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
|
||||
if (count < 0)
|
||||
break;
|
||||
pbuf += count;
|
||||
res -= count;
|
||||
if (ev.type != SNDRV_SEQ_EVENT_NONE) {
|
||||
ev.source.port = msynth->seq_port;
|
||||
ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
|
||||
/* clear event and reset header */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dump_midi(snd_rawmidi_substream_t *substream, const char *buf, int count)
|
||||
{
|
||||
snd_rawmidi_runtime_t *runtime;
|
||||
int tmp;
|
||||
|
||||
snd_assert(substream != NULL || buf != NULL, return -EINVAL);
|
||||
runtime = substream->runtime;
|
||||
if ((tmp = runtime->avail) < count) {
|
||||
snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int event_process_midi(snd_seq_event_t * ev, int direct,
|
||||
void *private_data, int atomic, int hop)
|
||||
{
|
||||
seq_midisynth_t *msynth = (seq_midisynth_t *) private_data;
|
||||
unsigned char msg[10]; /* buffer for constructing midi messages */
|
||||
snd_rawmidi_substream_t *substream;
|
||||
int res;
|
||||
|
||||
snd_assert(msynth != NULL, return -EINVAL);
|
||||
substream = msynth->output_rfile.output;
|
||||
if (substream == NULL)
|
||||
return -ENODEV;
|
||||
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
|
||||
/* invalid event */
|
||||
snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
|
||||
return 0;
|
||||
}
|
||||
res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
|
||||
snd_midi_event_reset_decode(msynth->parser);
|
||||
if (res < 0)
|
||||
return res;
|
||||
} else {
|
||||
if (msynth->parser == NULL)
|
||||
return -EIO;
|
||||
res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if ((res = dump_midi(substream, msg, res)) < 0) {
|
||||
snd_midi_event_reset_decode(msynth->parser);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_seq_midisynth_new(seq_midisynth_t *msynth,
|
||||
snd_card_t *card,
|
||||
int device,
|
||||
int subdevice)
|
||||
{
|
||||
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
|
||||
return -ENOMEM;
|
||||
msynth->card = card;
|
||||
msynth->device = device;
|
||||
msynth->subdevice = subdevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* open associated midi device for input */
|
||||
static int midisynth_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
int err;
|
||||
seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
|
||||
snd_rawmidi_runtime_t *runtime;
|
||||
snd_rawmidi_params_t params;
|
||||
|
||||
/* open midi port */
|
||||
if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, &msynth->input_rfile)) < 0) {
|
||||
snd_printd("midi input open failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
runtime = msynth->input_rfile.input->runtime;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.avail_min = 1;
|
||||
params.buffer_size = input_buffer_size;
|
||||
if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms)) < 0) {
|
||||
snd_rawmidi_kernel_release(&msynth->input_rfile);
|
||||
return err;
|
||||
}
|
||||
snd_midi_event_reset_encode(msynth->parser);
|
||||
runtime->event = snd_midi_input_event;
|
||||
runtime->private_data = msynth;
|
||||
snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close associated midi device for input */
|
||||
static int midisynth_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
int err;
|
||||
seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
|
||||
|
||||
snd_assert(msynth->input_rfile.input != NULL, return -EINVAL);
|
||||
err = snd_rawmidi_kernel_release(&msynth->input_rfile);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* open associated midi device for output */
|
||||
static int midisynth_use(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
int err;
|
||||
seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
|
||||
snd_rawmidi_params_t params;
|
||||
|
||||
/* open midi port */
|
||||
if ((err = snd_rawmidi_kernel_open(msynth->card->number, msynth->device, msynth->subdevice, SNDRV_RAWMIDI_LFLG_OUTPUT, &msynth->output_rfile)) < 0) {
|
||||
snd_printd("midi output open failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.avail_min = 1;
|
||||
params.buffer_size = output_buffer_size;
|
||||
if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) {
|
||||
snd_rawmidi_kernel_release(&msynth->output_rfile);
|
||||
return err;
|
||||
}
|
||||
snd_midi_event_reset_decode(msynth->parser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close associated midi device for output */
|
||||
static int midisynth_unuse(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
seq_midisynth_t *msynth = (seq_midisynth_t *)private_data;
|
||||
unsigned char buf = 0xff; /* MIDI reset */
|
||||
|
||||
snd_assert(msynth->output_rfile.output != NULL, return -EINVAL);
|
||||
/* sending single MIDI reset message to shut the device up */
|
||||
snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
|
||||
snd_rawmidi_drain_output(msynth->output_rfile.output);
|
||||
return snd_rawmidi_kernel_release(&msynth->output_rfile);
|
||||
}
|
||||
|
||||
/* delete given midi synth port */
|
||||
static void snd_seq_midisynth_delete(seq_midisynth_t *msynth)
|
||||
{
|
||||
if (msynth == NULL)
|
||||
return;
|
||||
|
||||
if (msynth->seq_client > 0) {
|
||||
/* delete port */
|
||||
snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
|
||||
}
|
||||
|
||||
if (msynth->parser)
|
||||
snd_midi_event_free(msynth->parser);
|
||||
}
|
||||
|
||||
/* set our client name */
|
||||
static int set_client_name(seq_midisynth_client_t *client, snd_card_t *card,
|
||||
snd_rawmidi_info_t *rmidi)
|
||||
{
|
||||
snd_seq_client_info_t cinfo;
|
||||
const char *name;
|
||||
|
||||
memset(&cinfo, 0, sizeof(cinfo));
|
||||
cinfo.client = client->seq_client;
|
||||
cinfo.type = KERNEL_CLIENT;
|
||||
name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI";
|
||||
strlcpy(cinfo.name, name, sizeof(cinfo.name));
|
||||
return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
|
||||
}
|
||||
|
||||
/* register new midi synth port */
|
||||
static int
|
||||
snd_seq_midisynth_register_port(snd_seq_device_t *dev)
|
||||
{
|
||||
seq_midisynth_client_t *client;
|
||||
seq_midisynth_t *msynth, *ms;
|
||||
snd_seq_port_info_t *port;
|
||||
snd_rawmidi_info_t *info;
|
||||
int newclient = 0;
|
||||
unsigned int p, ports;
|
||||
snd_seq_client_callback_t callbacks;
|
||||
snd_seq_port_callback_t pcallbacks;
|
||||
snd_card_t *card = dev->card;
|
||||
int device = dev->device;
|
||||
unsigned int input_count = 0, output_count = 0;
|
||||
|
||||
snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL);
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (! info)
|
||||
return -ENOMEM;
|
||||
info->device = device;
|
||||
info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
|
||||
info->subdevice = 0;
|
||||
if (snd_rawmidi_info_select(card, info) >= 0)
|
||||
output_count = info->subdevices_count;
|
||||
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
|
||||
if (snd_rawmidi_info_select(card, info) >= 0) {
|
||||
input_count = info->subdevices_count;
|
||||
}
|
||||
ports = output_count;
|
||||
if (ports < input_count)
|
||||
ports = input_count;
|
||||
if (ports == 0) {
|
||||
kfree(info);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
|
||||
ports = 256 / SNDRV_RAWMIDI_DEVICES;
|
||||
|
||||
down(®ister_mutex);
|
||||
client = synths[card->number];
|
||||
if (client == NULL) {
|
||||
newclient = 1;
|
||||
client = kcalloc(1, sizeof(*client), GFP_KERNEL);
|
||||
if (client == NULL) {
|
||||
up(®ister_mutex);
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.private_data = client;
|
||||
callbacks.allow_input = callbacks.allow_output = 1;
|
||||
client->seq_client = snd_seq_create_kernel_client(card, 0, &callbacks);
|
||||
if (client->seq_client < 0) {
|
||||
kfree(client);
|
||||
up(®ister_mutex);
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_client_name(client, card, info);
|
||||
} else if (device == 0)
|
||||
set_client_name(client, card, info); /* use the first device's name */
|
||||
|
||||
msynth = kcalloc(ports, sizeof(seq_midisynth_t), GFP_KERNEL);
|
||||
port = kmalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (msynth == NULL || port == NULL)
|
||||
goto __nomem;
|
||||
|
||||
for (p = 0; p < ports; p++) {
|
||||
ms = &msynth[p];
|
||||
|
||||
if (snd_seq_midisynth_new(ms, card, device, p) < 0)
|
||||
goto __nomem;
|
||||
|
||||
/* declare port */
|
||||
memset(port, 0, sizeof(*port));
|
||||
port->addr.client = client->seq_client;
|
||||
port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
|
||||
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->device = device;
|
||||
if (p < output_count)
|
||||
info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
|
||||
else
|
||||
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
|
||||
info->subdevice = p;
|
||||
if (snd_rawmidi_info_select(card, info) >= 0)
|
||||
strcpy(port->name, info->subname);
|
||||
if (! port->name[0]) {
|
||||
if (info->name[0]) {
|
||||
if (ports > 1)
|
||||
snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
|
||||
else
|
||||
snprintf(port->name, sizeof(port->name), "%s", info->name);
|
||||
} else {
|
||||
/* last resort */
|
||||
if (ports > 1)
|
||||
sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
|
||||
else
|
||||
sprintf(port->name, "MIDI %d-%d", card->number, device);
|
||||
}
|
||||
}
|
||||
if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
|
||||
port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
|
||||
if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
|
||||
port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
|
||||
if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
|
||||
info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
|
||||
port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
|
||||
port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
|
||||
port->midi_channels = 16;
|
||||
memset(&pcallbacks, 0, sizeof(pcallbacks));
|
||||
pcallbacks.owner = THIS_MODULE;
|
||||
pcallbacks.private_data = ms;
|
||||
pcallbacks.subscribe = midisynth_subscribe;
|
||||
pcallbacks.unsubscribe = midisynth_unsubscribe;
|
||||
pcallbacks.use = midisynth_use;
|
||||
pcallbacks.unuse = midisynth_unuse;
|
||||
pcallbacks.event_input = event_process_midi;
|
||||
port->kernel = &pcallbacks;
|
||||
if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
|
||||
goto __nomem;
|
||||
ms->seq_client = client->seq_client;
|
||||
ms->seq_port = port->addr.port;
|
||||
}
|
||||
client->ports_per_device[device] = ports;
|
||||
client->ports[device] = msynth;
|
||||
client->num_ports++;
|
||||
if (newclient)
|
||||
synths[card->number] = client;
|
||||
up(®ister_mutex);
|
||||
return 0; /* success */
|
||||
|
||||
__nomem:
|
||||
if (msynth != NULL) {
|
||||
for (p = 0; p < ports; p++)
|
||||
snd_seq_midisynth_delete(&msynth[p]);
|
||||
kfree(msynth);
|
||||
}
|
||||
if (newclient) {
|
||||
snd_seq_delete_kernel_client(client->seq_client);
|
||||
kfree(client);
|
||||
}
|
||||
kfree(info);
|
||||
kfree(port);
|
||||
up(®ister_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* release midi synth port */
|
||||
static int
|
||||
snd_seq_midisynth_unregister_port(snd_seq_device_t *dev)
|
||||
{
|
||||
seq_midisynth_client_t *client;
|
||||
seq_midisynth_t *msynth;
|
||||
snd_card_t *card = dev->card;
|
||||
int device = dev->device, p, ports;
|
||||
|
||||
down(®ister_mutex);
|
||||
client = synths[card->number];
|
||||
if (client == NULL || client->ports[device] == NULL) {
|
||||
up(®ister_mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
ports = client->ports_per_device[device];
|
||||
client->ports_per_device[device] = 0;
|
||||
msynth = client->ports[device];
|
||||
client->ports[device] = NULL;
|
||||
snd_runtime_check(msynth != NULL || ports <= 0, goto __skip);
|
||||
for (p = 0; p < ports; p++)
|
||||
snd_seq_midisynth_delete(&msynth[p]);
|
||||
kfree(msynth);
|
||||
__skip:
|
||||
client->num_ports--;
|
||||
if (client->num_ports <= 0) {
|
||||
snd_seq_delete_kernel_client(client->seq_client);
|
||||
synths[card->number] = NULL;
|
||||
kfree(client);
|
||||
}
|
||||
up(®ister_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __init alsa_seq_midi_init(void)
|
||||
{
|
||||
static snd_seq_dev_ops_t ops = {
|
||||
snd_seq_midisynth_register_port,
|
||||
snd_seq_midisynth_unregister_port,
|
||||
};
|
||||
memset(&synths, 0, sizeof(synths));
|
||||
snd_seq_autoload_lock();
|
||||
snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
|
||||
snd_seq_autoload_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_midi_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
|
||||
}
|
||||
|
||||
module_init(alsa_seq_midi_init)
|
||||
module_exit(alsa_seq_midi_exit)
|
||||
735
sound/core/seq/seq_midi_emul.c
Normal file
735
sound/core/seq/seq_midi_emul.c
Normal file
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* GM/GS/XG midi module.
|
||||
*
|
||||
* Copyright (C) 1999 Steve Ratcliffe
|
||||
*
|
||||
* Based on awe_wave.c by Takashi Iwai
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* This module is used to keep track of the current midi state.
|
||||
* It can be used for drivers that are required to emulate midi when
|
||||
* the hardware doesn't.
|
||||
*
|
||||
* It was written for a AWE64 driver, but there should be no AWE specific
|
||||
* code in here. If there is it should be reported as a bug.
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/seq_midi_emul.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/asoundef.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* Prototypes for static functions */
|
||||
static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel);
|
||||
static void do_control(snd_midi_op_t *ops, void *private,
|
||||
snd_midi_channel_set_t *chset, snd_midi_channel_t *chan,
|
||||
int control, int value);
|
||||
static void rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
|
||||
static void nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
|
||||
static void sysex(snd_midi_op_t *ops, void *private, unsigned char *sysex, int len, snd_midi_channel_set_t *chset);
|
||||
static void all_sounds_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
|
||||
static void all_notes_off(snd_midi_op_t *ops, void *private, snd_midi_channel_t *chan);
|
||||
static void snd_midi_reset_controllers(snd_midi_channel_t *chan);
|
||||
static void reset_all_channels(snd_midi_channel_set_t *chset);
|
||||
|
||||
|
||||
/*
|
||||
* Process an event in a driver independent way. This means dealing
|
||||
* with RPN, NRPN, SysEx etc that are defined for common midi applications
|
||||
* such as GM, GS and XG.
|
||||
* There modes that this module will run in are:
|
||||
* Generic MIDI - no interpretation at all, it will just save current values
|
||||
* of controlers etc.
|
||||
* GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN,
|
||||
* SysEx will be interpreded as defined in General Midi.
|
||||
* GS - You can use all gs_ prefixed elements of chan. Codes for GS will be
|
||||
* interpreted.
|
||||
* XG - You can use all xg_ prefixed elements of chan. Codes for XG will
|
||||
* be interpreted.
|
||||
*/
|
||||
void
|
||||
snd_midi_process_event(snd_midi_op_t *ops,
|
||||
snd_seq_event_t *ev, snd_midi_channel_set_t *chanset)
|
||||
{
|
||||
snd_midi_channel_t *chan;
|
||||
void *drv;
|
||||
int dest_channel = 0;
|
||||
|
||||
if (ev == NULL || chanset == NULL) {
|
||||
snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
|
||||
return;
|
||||
}
|
||||
if (chanset->channels == NULL)
|
||||
return;
|
||||
|
||||
if (snd_seq_ev_is_channel_type(ev)) {
|
||||
dest_channel = ev->data.note.channel;
|
||||
if (dest_channel >= chanset->max_channels) {
|
||||
snd_printd("dest channel is %d, max is %d\n", dest_channel, chanset->max_channels);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
chan = chanset->channels + dest_channel;
|
||||
drv = chanset->private_data;
|
||||
|
||||
/* EVENT_NOTE should be processed before queued */
|
||||
if (ev->type == SNDRV_SEQ_EVENT_NOTE)
|
||||
return;
|
||||
|
||||
/* Make sure that we don't have a note on that should really be
|
||||
* a note off */
|
||||
if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
|
||||
ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
|
||||
|
||||
/* Make sure the note is within array range */
|
||||
if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
|
||||
ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
|
||||
ev->type == SNDRV_SEQ_EVENT_KEYPRESS) {
|
||||
if (ev->data.note.note >= 128)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_NOTEON:
|
||||
if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) {
|
||||
if (ops->note_off)
|
||||
ops->note_off(drv, ev->data.note.note, 0, chan);
|
||||
}
|
||||
chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON;
|
||||
if (ops->note_on)
|
||||
ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_NOTEOFF:
|
||||
if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON))
|
||||
break;
|
||||
if (ops->note_off)
|
||||
note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_KEYPRESS:
|
||||
if (ops->key_press)
|
||||
ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_CONTROLLER:
|
||||
do_control(ops, drv, chanset, chan,
|
||||
ev->data.control.param, ev->data.control.value);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_PGMCHANGE:
|
||||
chan->midi_program = ev->data.control.value;
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_PITCHBEND:
|
||||
chan->midi_pitchbend = ev->data.control.value;
|
||||
if (ops->control)
|
||||
ops->control(drv, MIDI_CTL_PITCHBEND, chan);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_CHANPRESS:
|
||||
chan->midi_pressure = ev->data.control.value;
|
||||
if (ops->control)
|
||||
ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_CONTROL14:
|
||||
/* Best guess is that this is any of the 14 bit controller values */
|
||||
if (ev->data.control.param < 32) {
|
||||
/* set low part first */
|
||||
chan->control[ev->data.control.param + 32] =
|
||||
ev->data.control.value & 0x7f;
|
||||
do_control(ops, drv, chanset, chan,
|
||||
ev->data.control.param,
|
||||
((ev->data.control.value>>7) & 0x7f));
|
||||
} else
|
||||
do_control(ops, drv, chanset, chan,
|
||||
ev->data.control.param,
|
||||
ev->data.control.value);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_NONREGPARAM:
|
||||
/* Break it back into its controler values */
|
||||
chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
|
||||
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
|
||||
= (ev->data.control.value >> 7) & 0x7f;
|
||||
chan->control[MIDI_CTL_LSB_DATA_ENTRY]
|
||||
= ev->data.control.value & 0x7f;
|
||||
chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
|
||||
= (ev->data.control.param >> 7) & 0x7f;
|
||||
chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
|
||||
= ev->data.control.param & 0x7f;
|
||||
nrpn(ops, drv, chan, chanset);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_REGPARAM:
|
||||
/* Break it back into its controler values */
|
||||
chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
|
||||
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
|
||||
= (ev->data.control.value >> 7) & 0x7f;
|
||||
chan->control[MIDI_CTL_LSB_DATA_ENTRY]
|
||||
= ev->data.control.value & 0x7f;
|
||||
chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
|
||||
= (ev->data.control.param >> 7) & 0x7f;
|
||||
chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
|
||||
= ev->data.control.param & 0x7f;
|
||||
rpn(ops, drv, chan, chanset);
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_SYSEX:
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
|
||||
unsigned char sysexbuf[64];
|
||||
int len;
|
||||
len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0);
|
||||
if (len > 0)
|
||||
sysex(ops, drv, sysexbuf, len, chanset);
|
||||
}
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_SONGPOS:
|
||||
case SNDRV_SEQ_EVENT_SONGSEL:
|
||||
case SNDRV_SEQ_EVENT_CLOCK:
|
||||
case SNDRV_SEQ_EVENT_START:
|
||||
case SNDRV_SEQ_EVENT_CONTINUE:
|
||||
case SNDRV_SEQ_EVENT_STOP:
|
||||
case SNDRV_SEQ_EVENT_QFRAME:
|
||||
case SNDRV_SEQ_EVENT_TEMPO:
|
||||
case SNDRV_SEQ_EVENT_TIMESIGN:
|
||||
case SNDRV_SEQ_EVENT_KEYSIGN:
|
||||
goto not_yet;
|
||||
case SNDRV_SEQ_EVENT_SENSING:
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_CLIENT_START:
|
||||
case SNDRV_SEQ_EVENT_CLIENT_EXIT:
|
||||
case SNDRV_SEQ_EVENT_CLIENT_CHANGE:
|
||||
case SNDRV_SEQ_EVENT_PORT_START:
|
||||
case SNDRV_SEQ_EVENT_PORT_EXIT:
|
||||
case SNDRV_SEQ_EVENT_PORT_CHANGE:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_START:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_STOP:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_FREQ:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_VOLUME:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_LOOP:
|
||||
case SNDRV_SEQ_EVENT_SAMPLE_POSITION:
|
||||
case SNDRV_SEQ_EVENT_ECHO:
|
||||
not_yet:
|
||||
default:
|
||||
/*snd_printd("Unimplemented event %d\n", ev->type);*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release note
|
||||
*/
|
||||
static void
|
||||
note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel)
|
||||
{
|
||||
if (chan->gm_hold) {
|
||||
/* Hold this note until pedal is turned off */
|
||||
chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
|
||||
} else if (chan->note[note] & SNDRV_MIDI_NOTE_SOSTENUTO) {
|
||||
/* Mark this note as release; it will be turned off when sostenuto
|
||||
* is turned off */
|
||||
chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
|
||||
} else {
|
||||
chan->note[note] = 0;
|
||||
if (ops->note_off)
|
||||
ops->note_off(drv, note, vel, chan);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do all driver independent operations for this controler and pass
|
||||
* events that need to take place immediately to the driver.
|
||||
*/
|
||||
static void
|
||||
do_control(snd_midi_op_t *ops, void *drv, snd_midi_channel_set_t *chset,
|
||||
snd_midi_channel_t *chan, int control, int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Switches */
|
||||
if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
|
||||
/* These are all switches; either off or on so set to 0 or 127 */
|
||||
value = (value >= 64)? 127: 0;
|
||||
}
|
||||
chan->control[control] = value;
|
||||
|
||||
switch (control) {
|
||||
case MIDI_CTL_SUSTAIN:
|
||||
if (value == 0) {
|
||||
/* Sustain has been released, turn off held notes */
|
||||
for (i = 0; i < 128; i++) {
|
||||
if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
|
||||
chan->note[i] = SNDRV_MIDI_NOTE_OFF;
|
||||
if (ops->note_off)
|
||||
ops->note_off(drv, i, 0, chan);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MIDI_CTL_PORTAMENTO:
|
||||
break;
|
||||
case MIDI_CTL_SOSTENUTO:
|
||||
if (value) {
|
||||
/* Mark each note that is currently held down */
|
||||
for (i = 0; i < 128; i++) {
|
||||
if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
|
||||
chan->note[i] |= SNDRV_MIDI_NOTE_SOSTENUTO;
|
||||
}
|
||||
} else {
|
||||
/* release all notes that were held */
|
||||
for (i = 0; i < 128; i++) {
|
||||
if (chan->note[i] & SNDRV_MIDI_NOTE_SOSTENUTO) {
|
||||
chan->note[i] &= ~SNDRV_MIDI_NOTE_SOSTENUTO;
|
||||
if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
|
||||
chan->note[i] = SNDRV_MIDI_NOTE_OFF;
|
||||
if (ops->note_off)
|
||||
ops->note_off(drv, i, 0, chan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MIDI_CTL_MSB_DATA_ENTRY:
|
||||
chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
|
||||
/* go through here */
|
||||
case MIDI_CTL_LSB_DATA_ENTRY:
|
||||
if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
|
||||
rpn(ops, drv, chan, chset);
|
||||
else
|
||||
nrpn(ops, drv, chan, chset);
|
||||
break;
|
||||
case MIDI_CTL_REGIST_PARM_NUM_LSB:
|
||||
case MIDI_CTL_REGIST_PARM_NUM_MSB:
|
||||
chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
|
||||
break;
|
||||
case MIDI_CTL_NONREG_PARM_NUM_LSB:
|
||||
case MIDI_CTL_NONREG_PARM_NUM_MSB:
|
||||
chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
|
||||
break;
|
||||
|
||||
case MIDI_CTL_ALL_SOUNDS_OFF:
|
||||
all_sounds_off(ops, drv, chan);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_ALL_NOTES_OFF:
|
||||
all_notes_off(ops, drv, chan);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_MSB_BANK:
|
||||
if (chset->midi_mode == SNDRV_MIDI_MODE_XG) {
|
||||
if (value == 127)
|
||||
chan->drum_channel = 1;
|
||||
else
|
||||
chan->drum_channel = 0;
|
||||
}
|
||||
break;
|
||||
case MIDI_CTL_LSB_BANK:
|
||||
break;
|
||||
|
||||
case MIDI_CTL_RESET_CONTROLLERS:
|
||||
snd_midi_reset_controllers(chan);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_SOFT_PEDAL:
|
||||
case MIDI_CTL_LEGATO_FOOTSWITCH:
|
||||
case MIDI_CTL_HOLD2:
|
||||
case MIDI_CTL_SC1_SOUND_VARIATION:
|
||||
case MIDI_CTL_SC2_TIMBRE:
|
||||
case MIDI_CTL_SC3_RELEASE_TIME:
|
||||
case MIDI_CTL_SC4_ATTACK_TIME:
|
||||
case MIDI_CTL_SC5_BRIGHTNESS:
|
||||
case MIDI_CTL_E1_REVERB_DEPTH:
|
||||
case MIDI_CTL_E2_TREMOLO_DEPTH:
|
||||
case MIDI_CTL_E3_CHORUS_DEPTH:
|
||||
case MIDI_CTL_E4_DETUNE_DEPTH:
|
||||
case MIDI_CTL_E5_PHASER_DEPTH:
|
||||
goto notyet;
|
||||
notyet:
|
||||
default:
|
||||
if (ops->control)
|
||||
ops->control(drv, control, chan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialize the MIDI status
|
||||
*/
|
||||
void
|
||||
snd_midi_channel_set_clear(snd_midi_channel_set_t *chset)
|
||||
{
|
||||
int i;
|
||||
|
||||
chset->midi_mode = SNDRV_MIDI_MODE_GM;
|
||||
chset->gs_master_volume = 127;
|
||||
|
||||
for (i = 0; i < chset->max_channels; i++) {
|
||||
snd_midi_channel_t *chan = chset->channels + i;
|
||||
memset(chan->note, 0, sizeof(chan->note));
|
||||
|
||||
chan->midi_aftertouch = 0;
|
||||
chan->midi_pressure = 0;
|
||||
chan->midi_program = 0;
|
||||
chan->midi_pitchbend = 0;
|
||||
snd_midi_reset_controllers(chan);
|
||||
chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
|
||||
chan->gm_rpn_fine_tuning = 0;
|
||||
chan->gm_rpn_coarse_tuning = 0;
|
||||
|
||||
if (i == 9)
|
||||
chan->drum_channel = 1;
|
||||
else
|
||||
chan->drum_channel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a rpn message.
|
||||
*/
|
||||
static void
|
||||
rpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
|
||||
snd_midi_channel_set_t *chset)
|
||||
{
|
||||
int type;
|
||||
int val;
|
||||
|
||||
if (chset->midi_mode != SNDRV_MIDI_MODE_NONE) {
|
||||
type = (chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB] << 8) |
|
||||
chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB];
|
||||
val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
|
||||
chan->control[MIDI_CTL_LSB_DATA_ENTRY];
|
||||
|
||||
switch (type) {
|
||||
case 0x0000: /* Pitch bend sensitivity */
|
||||
/* MSB only / 1 semitone per 128 */
|
||||
chan->gm_rpn_pitch_bend_range = val;
|
||||
break;
|
||||
|
||||
case 0x0001: /* fine tuning: */
|
||||
/* MSB/LSB, 8192=center, 100/8192 cent step */
|
||||
chan->gm_rpn_fine_tuning = val - 8192;
|
||||
break;
|
||||
|
||||
case 0x0002: /* coarse tuning */
|
||||
/* MSB only / 8192=center, 1 semitone per 128 */
|
||||
chan->gm_rpn_coarse_tuning = val - 8192;
|
||||
break;
|
||||
|
||||
case 0x7F7F: /* "lock-in" RPN */
|
||||
/* ignored */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* should call nrpn or rpn callback here.. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Process an nrpn message.
|
||||
*/
|
||||
static void
|
||||
nrpn(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan,
|
||||
snd_midi_channel_set_t *chset)
|
||||
{
|
||||
/* parse XG NRPNs here if possible */
|
||||
if (ops->nrpn)
|
||||
ops->nrpn(drv, chan, chset);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* convert channel parameter in GS sysex
|
||||
*/
|
||||
static int
|
||||
get_channel(unsigned char cmd)
|
||||
{
|
||||
int p = cmd & 0x0f;
|
||||
if (p == 0)
|
||||
p = 9;
|
||||
else if (p < 10)
|
||||
p--;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process a sysex message.
|
||||
*/
|
||||
static void
|
||||
sysex(snd_midi_op_t *ops, void *private, unsigned char *buf, int len, snd_midi_channel_set_t *chset)
|
||||
{
|
||||
/* GM on */
|
||||
static unsigned char gm_on_macro[] = {
|
||||
0x7e,0x7f,0x09,0x01,
|
||||
};
|
||||
/* XG on */
|
||||
static unsigned char xg_on_macro[] = {
|
||||
0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
|
||||
};
|
||||
/* GS prefix
|
||||
* drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
|
||||
* reverb mode: XX=0x01, YY=0x30, ZZ=0-7
|
||||
* chorus mode: XX=0x01, YY=0x38, ZZ=0-7
|
||||
* master vol: XX=0x00, YY=0x04, ZZ=0-127
|
||||
*/
|
||||
static unsigned char gs_pfx_macro[] = {
|
||||
0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
|
||||
};
|
||||
|
||||
int parsed = SNDRV_MIDI_SYSEX_NOT_PARSED;
|
||||
|
||||
if (len <= 0 || buf[0] != 0xf0)
|
||||
return;
|
||||
/* skip first byte */
|
||||
buf++;
|
||||
len--;
|
||||
|
||||
/* GM on */
|
||||
if (len >= (int)sizeof(gm_on_macro) &&
|
||||
memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
|
||||
if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
|
||||
chset->midi_mode != SNDRV_MIDI_MODE_XG) {
|
||||
chset->midi_mode = SNDRV_MIDI_MODE_GM;
|
||||
reset_all_channels(chset);
|
||||
parsed = SNDRV_MIDI_SYSEX_GM_ON;
|
||||
}
|
||||
}
|
||||
|
||||
/* GS macros */
|
||||
else if (len >= 8 &&
|
||||
memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
|
||||
if (chset->midi_mode != SNDRV_MIDI_MODE_GS &&
|
||||
chset->midi_mode != SNDRV_MIDI_MODE_XG)
|
||||
chset->midi_mode = SNDRV_MIDI_MODE_GS;
|
||||
|
||||
if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
|
||||
/* GS reset */
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_RESET;
|
||||
reset_all_channels(chset);
|
||||
}
|
||||
|
||||
else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
|
||||
/* drum pattern */
|
||||
int p = get_channel(buf[5]);
|
||||
if (p < chset->max_channels) {
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
|
||||
if (buf[7])
|
||||
chset->channels[p].drum_channel = 1;
|
||||
else
|
||||
chset->channels[p].drum_channel = 0;
|
||||
}
|
||||
|
||||
} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
|
||||
/* program */
|
||||
int p = get_channel(buf[5]);
|
||||
if (p < chset->max_channels &&
|
||||
! chset->channels[p].drum_channel) {
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_DRUM_CHANNEL;
|
||||
chset->channels[p].midi_program = buf[7];
|
||||
}
|
||||
|
||||
} else if (buf[5] == 0x01 && buf[6] == 0x30) {
|
||||
/* reverb mode */
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_REVERB_MODE;
|
||||
chset->gs_reverb_mode = buf[7];
|
||||
|
||||
} else if (buf[5] == 0x01 && buf[6] == 0x38) {
|
||||
/* chorus mode */
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_CHORUS_MODE;
|
||||
chset->gs_chorus_mode = buf[7];
|
||||
|
||||
} else if (buf[5] == 0x00 && buf[6] == 0x04) {
|
||||
/* master volume */
|
||||
parsed = SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME;
|
||||
chset->gs_master_volume = buf[7];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* XG on */
|
||||
else if (len >= (int)sizeof(xg_on_macro) &&
|
||||
memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
|
||||
int i;
|
||||
chset->midi_mode = SNDRV_MIDI_MODE_XG;
|
||||
parsed = SNDRV_MIDI_SYSEX_XG_ON;
|
||||
/* reset CC#0 for drums */
|
||||
for (i = 0; i < chset->max_channels; i++) {
|
||||
if (chset->channels[i].drum_channel)
|
||||
chset->channels[i].control[MIDI_CTL_MSB_BANK] = 127;
|
||||
else
|
||||
chset->channels[i].control[MIDI_CTL_MSB_BANK] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ops->sysex)
|
||||
ops->sysex(private, buf - 1, len + 1, parsed, chset);
|
||||
}
|
||||
|
||||
/*
|
||||
* all sound off
|
||||
*/
|
||||
static void
|
||||
all_sounds_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (! ops->note_terminate)
|
||||
return;
|
||||
for (n = 0; n < 128; n++) {
|
||||
if (chan->note[n]) {
|
||||
ops->note_terminate(drv, n, chan);
|
||||
chan->note[n] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* all notes off
|
||||
*/
|
||||
static void
|
||||
all_notes_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (! ops->note_off)
|
||||
return;
|
||||
for (n = 0; n < 128; n++) {
|
||||
if (chan->note[n] == SNDRV_MIDI_NOTE_ON)
|
||||
note_off(ops, drv, chan, n, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise a single midi channel control block.
|
||||
*/
|
||||
static void snd_midi_channel_init(snd_midi_channel_t *p, int n)
|
||||
{
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
memset(p, 0, sizeof(snd_midi_channel_t));
|
||||
p->private = NULL;
|
||||
p->number = n;
|
||||
|
||||
snd_midi_reset_controllers(p);
|
||||
p->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
|
||||
p->gm_rpn_fine_tuning = 0;
|
||||
p->gm_rpn_coarse_tuning = 0;
|
||||
|
||||
if (n == 9)
|
||||
p->drum_channel = 1; /* Default ch 10 as drums */
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialise a set of midi channel control blocks.
|
||||
*/
|
||||
static snd_midi_channel_t *snd_midi_channel_init_set(int n)
|
||||
{
|
||||
snd_midi_channel_t *chan;
|
||||
int i;
|
||||
|
||||
chan = kmalloc(n * sizeof(snd_midi_channel_t), GFP_KERNEL);
|
||||
if (chan) {
|
||||
for (i = 0; i < n; i++)
|
||||
snd_midi_channel_init(chan+i, i);
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
/*
|
||||
* reset all midi channels
|
||||
*/
|
||||
static void
|
||||
reset_all_channels(snd_midi_channel_set_t *chset)
|
||||
{
|
||||
int ch;
|
||||
for (ch = 0; ch < chset->max_channels; ch++) {
|
||||
snd_midi_channel_t *chan = chset->channels + ch;
|
||||
snd_midi_reset_controllers(chan);
|
||||
chan->gm_rpn_pitch_bend_range = 256; /* 2 semitones */
|
||||
chan->gm_rpn_fine_tuning = 0;
|
||||
chan->gm_rpn_coarse_tuning = 0;
|
||||
|
||||
if (ch == 9)
|
||||
chan->drum_channel = 1;
|
||||
else
|
||||
chan->drum_channel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate and initialise a midi channel set.
|
||||
*/
|
||||
snd_midi_channel_set_t *snd_midi_channel_alloc_set(int n)
|
||||
{
|
||||
snd_midi_channel_set_t *chset;
|
||||
|
||||
chset = kmalloc(sizeof(*chset), GFP_KERNEL);
|
||||
if (chset) {
|
||||
chset->channels = snd_midi_channel_init_set(n);
|
||||
chset->private_data = NULL;
|
||||
chset->max_channels = n;
|
||||
}
|
||||
return chset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the midi controllers on a particular channel to default values.
|
||||
*/
|
||||
static void snd_midi_reset_controllers(snd_midi_channel_t *chan)
|
||||
{
|
||||
memset(chan->control, 0, sizeof(chan->control));
|
||||
chan->gm_volume = 127;
|
||||
chan->gm_expression = 127;
|
||||
chan->gm_pan = 64;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free a midi channel set.
|
||||
*/
|
||||
void snd_midi_channel_free_set(snd_midi_channel_set_t *chset)
|
||||
{
|
||||
if (chset == NULL)
|
||||
return;
|
||||
kfree(chset->channels);
|
||||
kfree(chset);
|
||||
}
|
||||
|
||||
static int __init alsa_seq_midi_emul_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_midi_emul_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_seq_midi_emul_init)
|
||||
module_exit(alsa_seq_midi_emul_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_midi_process_event);
|
||||
EXPORT_SYMBOL(snd_midi_channel_set_clear);
|
||||
EXPORT_SYMBOL(snd_midi_channel_alloc_set);
|
||||
EXPORT_SYMBOL(snd_midi_channel_free_set);
|
||||
539
sound/core/seq/seq_midi_event.c
Normal file
539
sound/core/seq/seq_midi_event.c
Normal file
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
* MIDI byte <-> sequencer event coder
|
||||
*
|
||||
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/seq_midi_event.h>
|
||||
#include <sound/asoundef.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* queue type */
|
||||
/* from 0 to 7 are normal commands (note off, on, etc.) */
|
||||
#define ST_NOTEOFF 0
|
||||
#define ST_NOTEON 1
|
||||
#define ST_SPECIAL 8
|
||||
#define ST_SYSEX ST_SPECIAL
|
||||
/* from 8 to 15 are events for 0xf0-0xf7 */
|
||||
|
||||
|
||||
/* status event types */
|
||||
typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
typedef void (*event_decode_t)(snd_seq_event_t *ev, unsigned char *buf);
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
|
||||
static void note_decode(snd_seq_event_t *ev, unsigned char *buf);
|
||||
static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf);
|
||||
static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf);
|
||||
static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf);
|
||||
static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf);
|
||||
|
||||
/*
|
||||
* event list
|
||||
*/
|
||||
static struct status_event_list_t {
|
||||
int event;
|
||||
int qlen;
|
||||
event_encode_t encode;
|
||||
event_decode_t decode;
|
||||
} status_event[] = {
|
||||
/* 0x80 - 0xf0 */
|
||||
{SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode},
|
||||
{SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode},
|
||||
{SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode},
|
||||
{SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode},
|
||||
{SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode},
|
||||
{SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode},
|
||||
{SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode},
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */
|
||||
/* 0xf0 - 0xff */
|
||||
{SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */
|
||||
{SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */
|
||||
{SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */
|
||||
{SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */
|
||||
{SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */
|
||||
{SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */
|
||||
{SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */
|
||||
{SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */
|
||||
{SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */
|
||||
{SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */
|
||||
{SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */
|
||||
{SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */
|
||||
};
|
||||
|
||||
static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
|
||||
static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev);
|
||||
|
||||
static struct extra_event_list_t {
|
||||
int event;
|
||||
int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, snd_seq_event_t *ev);
|
||||
} extra_event[] = {
|
||||
{SNDRV_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
|
||||
{SNDRV_SEQ_EVENT_NONREGPARAM, extra_decode_xrpn},
|
||||
{SNDRV_SEQ_EVENT_REGPARAM, extra_decode_xrpn},
|
||||
};
|
||||
|
||||
/*
|
||||
* new/delete record
|
||||
*/
|
||||
|
||||
int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev)
|
||||
{
|
||||
snd_midi_event_t *dev;
|
||||
|
||||
*rdev = NULL;
|
||||
dev = kcalloc(1, sizeof(*dev), GFP_KERNEL);
|
||||
if (dev == NULL)
|
||||
return -ENOMEM;
|
||||
if (bufsize > 0) {
|
||||
dev->buf = kmalloc(bufsize, GFP_KERNEL);
|
||||
if (dev->buf == NULL) {
|
||||
kfree(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
dev->bufsize = bufsize;
|
||||
dev->lastcmd = 0xff;
|
||||
spin_lock_init(&dev->lock);
|
||||
*rdev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_midi_event_free(snd_midi_event_t *dev)
|
||||
{
|
||||
if (dev != NULL) {
|
||||
kfree(dev->buf);
|
||||
kfree(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize record
|
||||
*/
|
||||
inline static void reset_encode(snd_midi_event_t *dev)
|
||||
{
|
||||
dev->read = 0;
|
||||
dev->qlen = 0;
|
||||
dev->type = 0;
|
||||
}
|
||||
|
||||
void snd_midi_event_reset_encode(snd_midi_event_t *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
reset_encode(dev);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
void snd_midi_event_reset_decode(snd_midi_event_t *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
dev->lastcmd = 0xff;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
void snd_midi_event_init(snd_midi_event_t *dev)
|
||||
{
|
||||
snd_midi_event_reset_encode(dev);
|
||||
snd_midi_event_reset_decode(dev);
|
||||
}
|
||||
|
||||
void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
|
||||
{
|
||||
dev->nostat = on ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* resize buffer
|
||||
*/
|
||||
int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
|
||||
{
|
||||
unsigned char *new_buf, *old_buf;
|
||||
unsigned long flags;
|
||||
|
||||
if (bufsize == dev->bufsize)
|
||||
return 0;
|
||||
new_buf = kmalloc(bufsize, GFP_KERNEL);
|
||||
if (new_buf == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
old_buf = dev->buf;
|
||||
dev->buf = new_buf;
|
||||
dev->bufsize = bufsize;
|
||||
reset_encode(dev);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
kfree(old_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* read bytes and encode to sequencer event if finished
|
||||
* return the size of encoded bytes
|
||||
*/
|
||||
long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
|
||||
{
|
||||
long result = 0;
|
||||
int rc;
|
||||
|
||||
ev->type = SNDRV_SEQ_EVENT_NONE;
|
||||
|
||||
while (count-- > 0) {
|
||||
rc = snd_midi_event_encode_byte(dev, *buf++, ev);
|
||||
result++;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
else if (rc > 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* read one byte and encode to sequencer event:
|
||||
* return 1 if MIDI bytes are encoded to an event
|
||||
* 0 data is not finished
|
||||
* negative for error
|
||||
*/
|
||||
int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned long flags;
|
||||
|
||||
c &= 0xff;
|
||||
|
||||
if (c >= MIDI_CMD_COMMON_CLOCK) {
|
||||
/* real-time event */
|
||||
ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
|
||||
ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->qlen > 0) {
|
||||
/* rest of command */
|
||||
dev->buf[dev->read++] = c;
|
||||
if (dev->type != ST_SYSEX)
|
||||
dev->qlen--;
|
||||
} else {
|
||||
/* new command */
|
||||
dev->read = 1;
|
||||
if (c & 0x80) {
|
||||
dev->buf[0] = c;
|
||||
if ((c & 0xf0) == 0xf0) /* special events */
|
||||
dev->type = (c & 0x0f) + ST_SPECIAL;
|
||||
else
|
||||
dev->type = (c >> 4) & 0x07;
|
||||
dev->qlen = status_event[dev->type].qlen;
|
||||
} else {
|
||||
/* process this byte as argument */
|
||||
dev->buf[dev->read++] = c;
|
||||
dev->qlen = status_event[dev->type].qlen - 1;
|
||||
}
|
||||
}
|
||||
if (dev->qlen == 0) {
|
||||
ev->type = status_event[dev->type].event;
|
||||
ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
|
||||
if (status_event[dev->type].encode) /* set data values */
|
||||
status_event[dev->type].encode(dev, ev);
|
||||
rc = 1;
|
||||
} else if (dev->type == ST_SYSEX) {
|
||||
if (c == MIDI_CMD_COMMON_SYSEX_END ||
|
||||
dev->read >= dev->bufsize) {
|
||||
ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
|
||||
ev->type = SNDRV_SEQ_EVENT_SYSEX;
|
||||
ev->data.ext.len = dev->read;
|
||||
ev->data.ext.ptr = dev->buf;
|
||||
if (c != MIDI_CMD_COMMON_SYSEX_END)
|
||||
dev->read = 0; /* continue to parse */
|
||||
else
|
||||
reset_encode(dev); /* all parsed */
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* encode note event */
|
||||
static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.note.channel = dev->buf[0] & 0x0f;
|
||||
ev->data.note.note = dev->buf[1];
|
||||
ev->data.note.velocity = dev->buf[2];
|
||||
}
|
||||
|
||||
/* encode one parameter controls */
|
||||
static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.control.channel = dev->buf[0] & 0x0f;
|
||||
ev->data.control.value = dev->buf[1];
|
||||
}
|
||||
|
||||
/* encode pitch wheel change */
|
||||
static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.control.channel = dev->buf[0] & 0x0f;
|
||||
ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
|
||||
}
|
||||
|
||||
/* encode midi control change */
|
||||
static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.control.channel = dev->buf[0] & 0x0f;
|
||||
ev->data.control.param = dev->buf[1];
|
||||
ev->data.control.value = dev->buf[2];
|
||||
}
|
||||
|
||||
/* encode one parameter value*/
|
||||
static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.control.value = dev->buf[1];
|
||||
}
|
||||
|
||||
/* encode song position */
|
||||
static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* decode from a sequencer event to midi bytes
|
||||
* return the size of decoded midi events
|
||||
*/
|
||||
long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev)
|
||||
{
|
||||
unsigned int cmd, type;
|
||||
|
||||
if (ev->type == SNDRV_SEQ_EVENT_NONE)
|
||||
return -ENOENT;
|
||||
|
||||
for (type = 0; type < ARRAY_SIZE(status_event); type++) {
|
||||
if (ev->type == status_event[type].event)
|
||||
goto __found;
|
||||
}
|
||||
for (type = 0; type < ARRAY_SIZE(extra_event); type++) {
|
||||
if (ev->type == extra_event[type].event)
|
||||
return extra_event[type].decode(dev, buf, count, ev);
|
||||
}
|
||||
return -ENOENT;
|
||||
|
||||
__found:
|
||||
if (type >= ST_SPECIAL)
|
||||
cmd = 0xf0 + (type - ST_SPECIAL);
|
||||
else
|
||||
/* data.note.channel and data.control.channel is identical */
|
||||
cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
|
||||
|
||||
|
||||
if (cmd == MIDI_CMD_COMMON_SYSEX) {
|
||||
snd_midi_event_reset_decode(dev);
|
||||
return snd_seq_expand_var_event(ev, count, buf, 1, 0);
|
||||
} else {
|
||||
int qlen;
|
||||
unsigned char xbuf[4];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
|
||||
dev->lastcmd = cmd;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
xbuf[0] = cmd;
|
||||
if (status_event[type].decode)
|
||||
status_event[type].decode(ev, xbuf + 1);
|
||||
qlen = status_event[type].qlen + 1;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (status_event[type].decode)
|
||||
status_event[type].decode(ev, xbuf + 0);
|
||||
qlen = status_event[type].qlen;
|
||||
}
|
||||
if (count < qlen)
|
||||
return -ENOMEM;
|
||||
memcpy(buf, xbuf, qlen);
|
||||
return qlen;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* decode note event */
|
||||
static void note_decode(snd_seq_event_t *ev, unsigned char *buf)
|
||||
{
|
||||
buf[0] = ev->data.note.note & 0x7f;
|
||||
buf[1] = ev->data.note.velocity & 0x7f;
|
||||
}
|
||||
|
||||
/* decode one parameter controls */
|
||||
static void one_param_decode(snd_seq_event_t *ev, unsigned char *buf)
|
||||
{
|
||||
buf[0] = ev->data.control.value & 0x7f;
|
||||
}
|
||||
|
||||
/* decode pitch wheel change */
|
||||
static void pitchbend_decode(snd_seq_event_t *ev, unsigned char *buf)
|
||||
{
|
||||
int value = ev->data.control.value + 8192;
|
||||
buf[0] = value & 0x7f;
|
||||
buf[1] = (value >> 7) & 0x7f;
|
||||
}
|
||||
|
||||
/* decode midi control change */
|
||||
static void two_param_decode(snd_seq_event_t *ev, unsigned char *buf)
|
||||
{
|
||||
buf[0] = ev->data.control.param & 0x7f;
|
||||
buf[1] = ev->data.control.value & 0x7f;
|
||||
}
|
||||
|
||||
/* decode song position */
|
||||
static void songpos_decode(snd_seq_event_t *ev, unsigned char *buf)
|
||||
{
|
||||
buf[0] = ev->data.control.value & 0x7f;
|
||||
buf[1] = (ev->data.control.value >> 7) & 0x7f;
|
||||
}
|
||||
|
||||
/* decode 14bit control */
|
||||
static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
|
||||
{
|
||||
unsigned char cmd;
|
||||
int idx = 0;
|
||||
|
||||
cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
|
||||
if (ev->data.control.param < 0x20) {
|
||||
if (count < 4)
|
||||
return -ENOMEM;
|
||||
if (dev->nostat && count < 6)
|
||||
return -ENOMEM;
|
||||
if (cmd != dev->lastcmd || dev->nostat) {
|
||||
if (count < 5)
|
||||
return -ENOMEM;
|
||||
buf[idx++] = dev->lastcmd = cmd;
|
||||
}
|
||||
buf[idx++] = ev->data.control.param;
|
||||
buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
|
||||
if (dev->nostat)
|
||||
buf[idx++] = cmd;
|
||||
buf[idx++] = ev->data.control.param + 0x20;
|
||||
buf[idx++] = ev->data.control.value & 0x7f;
|
||||
} else {
|
||||
if (count < 2)
|
||||
return -ENOMEM;
|
||||
if (cmd != dev->lastcmd || dev->nostat) {
|
||||
if (count < 3)
|
||||
return -ENOMEM;
|
||||
buf[idx++] = dev->lastcmd = cmd;
|
||||
}
|
||||
buf[idx++] = ev->data.control.param & 0x7f;
|
||||
buf[idx++] = ev->data.control.value & 0x7f;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* decode reg/nonreg param */
|
||||
static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
|
||||
{
|
||||
unsigned char cmd;
|
||||
char *cbytes;
|
||||
static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
|
||||
MIDI_CTL_NONREG_PARM_NUM_LSB,
|
||||
MIDI_CTL_MSB_DATA_ENTRY,
|
||||
MIDI_CTL_LSB_DATA_ENTRY };
|
||||
static char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
|
||||
MIDI_CTL_REGIST_PARM_NUM_LSB,
|
||||
MIDI_CTL_MSB_DATA_ENTRY,
|
||||
MIDI_CTL_LSB_DATA_ENTRY };
|
||||
unsigned char bytes[4];
|
||||
int idx = 0, i;
|
||||
|
||||
if (count < 8)
|
||||
return -ENOMEM;
|
||||
if (dev->nostat && count < 12)
|
||||
return -ENOMEM;
|
||||
cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
|
||||
bytes[0] = ev->data.control.param & 0x007f;
|
||||
bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
|
||||
bytes[2] = ev->data.control.value & 0x007f;
|
||||
bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
|
||||
if (cmd != dev->lastcmd && !dev->nostat) {
|
||||
if (count < 9)
|
||||
return -ENOMEM;
|
||||
buf[idx++] = dev->lastcmd = cmd;
|
||||
}
|
||||
cbytes = ev->type == SNDRV_SEQ_EVENT_NONREGPARAM ? cbytes_nrpn : cbytes_rpn;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (dev->nostat)
|
||||
buf[idx++] = dev->lastcmd = cmd;
|
||||
buf[idx++] = cbytes[i];
|
||||
buf[idx++] = bytes[i];
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* exports
|
||||
*/
|
||||
|
||||
EXPORT_SYMBOL(snd_midi_event_new);
|
||||
EXPORT_SYMBOL(snd_midi_event_free);
|
||||
EXPORT_SYMBOL(snd_midi_event_resize_buffer);
|
||||
EXPORT_SYMBOL(snd_midi_event_init);
|
||||
EXPORT_SYMBOL(snd_midi_event_reset_encode);
|
||||
EXPORT_SYMBOL(snd_midi_event_reset_decode);
|
||||
EXPORT_SYMBOL(snd_midi_event_no_status);
|
||||
EXPORT_SYMBOL(snd_midi_event_encode);
|
||||
EXPORT_SYMBOL(snd_midi_event_encode_byte);
|
||||
EXPORT_SYMBOL(snd_midi_event_decode);
|
||||
|
||||
static int __init alsa_seq_midi_event_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_seq_midi_event_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_seq_midi_event_init)
|
||||
module_exit(alsa_seq_midi_event_exit)
|
||||
674
sound/core/seq/seq_ports.c
Normal file
674
sound/core/seq/seq_ports.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* ALSA sequencer Ports
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include "seq_system.h"
|
||||
#include "seq_ports.h"
|
||||
#include "seq_clientmgr.h"
|
||||
|
||||
/*
|
||||
|
||||
registration of client ports
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
NOTE: the current implementation of the port structure as a linked list is
|
||||
not optimal for clients that have many ports. For sending messages to all
|
||||
subscribers of a port we first need to find the address of the port
|
||||
structure, which means we have to traverse the list. A direct access table
|
||||
(array) would be better, but big preallocated arrays waste memory.
|
||||
|
||||
Possible actions:
|
||||
|
||||
1) leave it this way, a client does normaly does not have more than a few
|
||||
ports
|
||||
|
||||
2) replace the linked list of ports by a array of pointers which is
|
||||
dynamicly kmalloced. When a port is added or deleted we can simply allocate
|
||||
a new array, copy the corresponding pointers, and delete the old one. We
|
||||
then only need a pointer to this array, and an integer that tells us how
|
||||
much elements are in array.
|
||||
|
||||
*/
|
||||
|
||||
/* return pointer to port structure - port is locked if found */
|
||||
client_port_t *snd_seq_port_use_ptr(client_t *client, int num)
|
||||
{
|
||||
struct list_head *p;
|
||||
client_port_t *port;
|
||||
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
read_lock(&client->ports_lock);
|
||||
list_for_each(p, &client->ports_list_head) {
|
||||
port = list_entry(p, client_port_t, list);
|
||||
if (port->addr.port == num) {
|
||||
if (port->closing)
|
||||
break; /* deleting now */
|
||||
snd_use_lock_use(&port->use_lock);
|
||||
read_unlock(&client->ports_lock);
|
||||
return port;
|
||||
}
|
||||
}
|
||||
read_unlock(&client->ports_lock);
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
|
||||
/* search for the next port - port is locked if found */
|
||||
client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo)
|
||||
{
|
||||
int num;
|
||||
struct list_head *p;
|
||||
client_port_t *port, *found;
|
||||
|
||||
num = pinfo->addr.port;
|
||||
found = NULL;
|
||||
read_lock(&client->ports_lock);
|
||||
list_for_each(p, &client->ports_list_head) {
|
||||
port = list_entry(p, client_port_t, list);
|
||||
if (port->addr.port < num)
|
||||
continue;
|
||||
if (port->addr.port == num) {
|
||||
found = port;
|
||||
break;
|
||||
}
|
||||
if (found == NULL || port->addr.port < found->addr.port)
|
||||
found = port;
|
||||
}
|
||||
if (found) {
|
||||
if (found->closing)
|
||||
found = NULL;
|
||||
else
|
||||
snd_use_lock_use(&found->use_lock);
|
||||
}
|
||||
read_unlock(&client->ports_lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/* initialize port_subs_info_t */
|
||||
static void port_subs_info_init(port_subs_info_t *grp)
|
||||
{
|
||||
INIT_LIST_HEAD(&grp->list_head);
|
||||
grp->count = 0;
|
||||
grp->exclusive = 0;
|
||||
rwlock_init(&grp->list_lock);
|
||||
init_rwsem(&grp->list_mutex);
|
||||
grp->open = NULL;
|
||||
grp->close = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* create a port, port number is returned (-1 on failure) */
|
||||
client_port_t *snd_seq_create_port(client_t *client, int port)
|
||||
{
|
||||
unsigned long flags;
|
||||
client_port_t *new_port;
|
||||
struct list_head *l;
|
||||
int num = -1;
|
||||
|
||||
/* sanity check */
|
||||
snd_assert(client, return NULL);
|
||||
|
||||
if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
|
||||
snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create a new port */
|
||||
new_port = kcalloc(1, sizeof(*new_port), GFP_KERNEL);
|
||||
if (! new_port) {
|
||||
snd_printd("malloc failed for registering client port\n");
|
||||
return NULL; /* failure, out of memory */
|
||||
}
|
||||
/* init port data */
|
||||
new_port->addr.client = client->number;
|
||||
new_port->addr.port = -1;
|
||||
new_port->owner = THIS_MODULE;
|
||||
sprintf(new_port->name, "port-%d", num);
|
||||
snd_use_lock_init(&new_port->use_lock);
|
||||
port_subs_info_init(&new_port->c_src);
|
||||
port_subs_info_init(&new_port->c_dest);
|
||||
|
||||
num = port >= 0 ? port : 0;
|
||||
down(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
list_for_each(l, &client->ports_list_head) {
|
||||
client_port_t *p = list_entry(l, client_port_t, list);
|
||||
if (p->addr.port > num)
|
||||
break;
|
||||
if (port < 0) /* auto-probe mode */
|
||||
num = p->addr.port + 1;
|
||||
}
|
||||
/* insert the new port */
|
||||
list_add_tail(&new_port->list, l);
|
||||
client->num_ports++;
|
||||
new_port->addr.port = num; /* store the port number in the port */
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
up(&client->ports_mutex);
|
||||
sprintf(new_port->name, "port-%d", num);
|
||||
|
||||
return new_port;
|
||||
}
|
||||
|
||||
/* */
|
||||
enum group_type_t {
|
||||
SRC_LIST, DEST_LIST
|
||||
};
|
||||
|
||||
static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
|
||||
static int unsubscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp, snd_seq_port_subscribe_t *info, int send_ack);
|
||||
|
||||
|
||||
static client_port_t *get_client_port(snd_seq_addr_t *addr, client_t **cp)
|
||||
{
|
||||
client_port_t *p;
|
||||
*cp = snd_seq_client_use_ptr(addr->client);
|
||||
if (*cp) {
|
||||
p = snd_seq_port_use_ptr(*cp, addr->port);
|
||||
if (! p) {
|
||||
snd_seq_client_unlock(*cp);
|
||||
*cp = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove all subscribers on the list
|
||||
* this is called from port_delete, for each src and dest list.
|
||||
*/
|
||||
static void clear_subscriber_list(client_t *client, client_port_t *port,
|
||||
port_subs_info_t *grp, int grptype)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
down_write(&grp->list_mutex);
|
||||
list_for_each_safe(p, n, &grp->list_head) {
|
||||
subscribers_t *subs;
|
||||
client_t *c;
|
||||
client_port_t *aport;
|
||||
|
||||
if (grptype == SRC_LIST) {
|
||||
subs = list_entry(p, subscribers_t, src_list);
|
||||
aport = get_client_port(&subs->info.dest, &c);
|
||||
} else {
|
||||
subs = list_entry(p, subscribers_t, dest_list);
|
||||
aport = get_client_port(&subs->info.sender, &c);
|
||||
}
|
||||
list_del(p);
|
||||
unsubscribe_port(client, port, grp, &subs->info, 0);
|
||||
if (!aport) {
|
||||
/* looks like the connected port is being deleted.
|
||||
* we decrease the counter, and when both ports are deleted
|
||||
* remove the subscriber info
|
||||
*/
|
||||
if (atomic_dec_and_test(&subs->ref_count))
|
||||
kfree(subs);
|
||||
} else {
|
||||
/* ok we got the connected port */
|
||||
port_subs_info_t *agrp;
|
||||
agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
|
||||
down_write(&agrp->list_mutex);
|
||||
if (grptype == SRC_LIST)
|
||||
list_del(&subs->dest_list);
|
||||
else
|
||||
list_del(&subs->src_list);
|
||||
unsubscribe_port(c, aport, agrp, &subs->info, 1);
|
||||
kfree(subs);
|
||||
up_write(&agrp->list_mutex);
|
||||
snd_seq_port_unlock(aport);
|
||||
snd_seq_client_unlock(c);
|
||||
}
|
||||
}
|
||||
up_write(&grp->list_mutex);
|
||||
}
|
||||
|
||||
/* delete port data */
|
||||
static int port_delete(client_t *client, client_port_t *port)
|
||||
{
|
||||
/* set closing flag and wait for all port access are gone */
|
||||
port->closing = 1;
|
||||
snd_use_lock_sync(&port->use_lock);
|
||||
|
||||
/* clear subscribers info */
|
||||
clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
|
||||
clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
|
||||
|
||||
if (port->private_free)
|
||||
port->private_free(port->private_data);
|
||||
|
||||
snd_assert(port->c_src.count == 0,);
|
||||
snd_assert(port->c_dest.count == 0,);
|
||||
|
||||
kfree(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* delete a port with the given port id */
|
||||
int snd_seq_delete_port(client_t *client, int port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *l;
|
||||
client_port_t *found = NULL;
|
||||
|
||||
down(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
list_for_each(l, &client->ports_list_head) {
|
||||
client_port_t *p = list_entry(l, client_port_t, list);
|
||||
if (p->addr.port == port) {
|
||||
/* ok found. delete from the list at first */
|
||||
list_del(l);
|
||||
client->num_ports--;
|
||||
found = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
up(&client->ports_mutex);
|
||||
if (found)
|
||||
return port_delete(client, found);
|
||||
else
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* delete the all ports belonging to the given client */
|
||||
int snd_seq_delete_all_ports(client_t *client)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head deleted_list, *p, *n;
|
||||
|
||||
/* move the port list to deleted_list, and
|
||||
* clear the port list in the client data.
|
||||
*/
|
||||
down(&client->ports_mutex);
|
||||
write_lock_irqsave(&client->ports_lock, flags);
|
||||
if (! list_empty(&client->ports_list_head)) {
|
||||
__list_add(&deleted_list,
|
||||
client->ports_list_head.prev,
|
||||
client->ports_list_head.next);
|
||||
INIT_LIST_HEAD(&client->ports_list_head);
|
||||
} else {
|
||||
INIT_LIST_HEAD(&deleted_list);
|
||||
}
|
||||
client->num_ports = 0;
|
||||
write_unlock_irqrestore(&client->ports_lock, flags);
|
||||
|
||||
/* remove each port in deleted_list */
|
||||
list_for_each_safe(p, n, &deleted_list) {
|
||||
client_port_t *port = list_entry(p, client_port_t, list);
|
||||
list_del(p);
|
||||
snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
|
||||
port_delete(client, port);
|
||||
}
|
||||
up(&client->ports_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set port info fields */
|
||||
int snd_seq_set_port_info(client_port_t * port, snd_seq_port_info_t * info)
|
||||
{
|
||||
snd_assert(port && info, return -EINVAL);
|
||||
|
||||
/* set port name */
|
||||
if (info->name[0])
|
||||
strlcpy(port->name, info->name, sizeof(port->name));
|
||||
|
||||
/* set capabilities */
|
||||
port->capability = info->capability;
|
||||
|
||||
/* get port type */
|
||||
port->type = info->type;
|
||||
|
||||
/* information about supported channels/voices */
|
||||
port->midi_channels = info->midi_channels;
|
||||
port->midi_voices = info->midi_voices;
|
||||
port->synth_voices = info->synth_voices;
|
||||
|
||||
/* timestamping */
|
||||
port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
|
||||
port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
|
||||
port->time_queue = info->time_queue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get port info fields */
|
||||
int snd_seq_get_port_info(client_port_t * port, snd_seq_port_info_t * info)
|
||||
{
|
||||
snd_assert(port && info, return -EINVAL);
|
||||
|
||||
/* get port name */
|
||||
strlcpy(info->name, port->name, sizeof(info->name));
|
||||
|
||||
/* get capabilities */
|
||||
info->capability = port->capability;
|
||||
|
||||
/* get port type */
|
||||
info->type = port->type;
|
||||
|
||||
/* information about supported channels/voices */
|
||||
info->midi_channels = port->midi_channels;
|
||||
info->midi_voices = port->midi_voices;
|
||||
info->synth_voices = port->synth_voices;
|
||||
|
||||
/* get subscriber counts */
|
||||
info->read_use = port->c_src.count;
|
||||
info->write_use = port->c_dest.count;
|
||||
|
||||
/* timestamping */
|
||||
info->flags = 0;
|
||||
if (port->timestamping) {
|
||||
info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
|
||||
if (port->time_real)
|
||||
info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
|
||||
info->time_queue = port->time_queue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* call callback functions (if any):
|
||||
* the callbacks are invoked only when the first (for connection) or
|
||||
* the last subscription (for disconnection) is done. Second or later
|
||||
* subscription results in increment of counter, but no callback is
|
||||
* invoked.
|
||||
* This feature is useful if these callbacks are associated with
|
||||
* initialization or termination of devices (see seq_midi.c).
|
||||
*
|
||||
* If callback_all option is set, the callback function is invoked
|
||||
* at each connnection/disconnection.
|
||||
*/
|
||||
|
||||
static int subscribe_port(client_t *client, client_port_t *port, port_subs_info_t *grp,
|
||||
snd_seq_port_subscribe_t *info, int send_ack)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!try_module_get(port->owner))
|
||||
return -EFAULT;
|
||||
grp->count++;
|
||||
if (grp->open && (port->callback_all || grp->count == 1)) {
|
||||
err = grp->open(port->private_data, info);
|
||||
if (err < 0) {
|
||||
module_put(port->owner);
|
||||
grp->count--;
|
||||
}
|
||||
}
|
||||
if (err >= 0 && send_ack && client->type == USER_CLIENT)
|
||||
snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
|
||||
info, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int unsubscribe_port(client_t *client, client_port_t *port,
|
||||
port_subs_info_t *grp,
|
||||
snd_seq_port_subscribe_t *info, int send_ack)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (! grp->count)
|
||||
return -EINVAL;
|
||||
grp->count--;
|
||||
if (grp->close && (port->callback_all || grp->count == 0))
|
||||
err = grp->close(port->private_data, info);
|
||||
if (send_ack && client->type == USER_CLIENT)
|
||||
snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
|
||||
info, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
|
||||
module_put(port->owner);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* check if both addresses are identical */
|
||||
static inline int addr_match(snd_seq_addr_t *r, snd_seq_addr_t *s)
|
||||
{
|
||||
return (r->client == s->client) && (r->port == s->port);
|
||||
}
|
||||
|
||||
/* check the two subscribe info match */
|
||||
/* if flags is zero, checks only sender and destination addresses */
|
||||
static int match_subs_info(snd_seq_port_subscribe_t *r,
|
||||
snd_seq_port_subscribe_t *s)
|
||||
{
|
||||
if (addr_match(&r->sender, &s->sender) &&
|
||||
addr_match(&r->dest, &s->dest)) {
|
||||
if (r->flags && r->flags == s->flags)
|
||||
return r->queue == s->queue;
|
||||
else if (! r->flags)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* connect two ports */
|
||||
int snd_seq_port_connect(client_t *connector,
|
||||
client_t *src_client, client_port_t *src_port,
|
||||
client_t *dest_client, client_port_t *dest_port,
|
||||
snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
port_subs_info_t *src = &src_port->c_src;
|
||||
port_subs_info_t *dest = &dest_port->c_dest;
|
||||
subscribers_t *subs;
|
||||
struct list_head *p;
|
||||
int err, src_called = 0;
|
||||
unsigned long flags;
|
||||
int exclusive;
|
||||
|
||||
subs = kcalloc(1, sizeof(*subs), GFP_KERNEL);
|
||||
if (! subs)
|
||||
return -ENOMEM;
|
||||
|
||||
subs->info = *info;
|
||||
atomic_set(&subs->ref_count, 2);
|
||||
|
||||
down_write(&src->list_mutex);
|
||||
down_write(&dest->list_mutex);
|
||||
|
||||
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
|
||||
err = -EBUSY;
|
||||
if (exclusive) {
|
||||
if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
|
||||
goto __error;
|
||||
} else {
|
||||
if (src->exclusive || dest->exclusive)
|
||||
goto __error;
|
||||
/* check whether already exists */
|
||||
list_for_each(p, &src->list_head) {
|
||||
subscribers_t *s = list_entry(p, subscribers_t, src_list);
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
list_for_each(p, &dest->list_head) {
|
||||
subscribers_t *s = list_entry(p, subscribers_t, dest_list);
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = subscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number)) < 0)
|
||||
goto __error;
|
||||
src_called = 1;
|
||||
|
||||
if ((err = subscribe_port(dest_client, dest_port, dest, info,
|
||||
connector->number != dest_client->number)) < 0)
|
||||
goto __error;
|
||||
|
||||
/* add to list */
|
||||
write_lock_irqsave(&src->list_lock, flags);
|
||||
// write_lock(&dest->list_lock); // no other lock yet
|
||||
list_add_tail(&subs->src_list, &src->list_head);
|
||||
list_add_tail(&subs->dest_list, &dest->list_head);
|
||||
// write_unlock(&dest->list_lock); // no other lock yet
|
||||
write_unlock_irqrestore(&src->list_lock, flags);
|
||||
|
||||
src->exclusive = dest->exclusive = exclusive;
|
||||
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
if (src_called)
|
||||
unsubscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number);
|
||||
kfree(subs);
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* remove the connection */
|
||||
int snd_seq_port_disconnect(client_t *connector,
|
||||
client_t *src_client, client_port_t *src_port,
|
||||
client_t *dest_client, client_port_t *dest_port,
|
||||
snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
port_subs_info_t *src = &src_port->c_src;
|
||||
port_subs_info_t *dest = &dest_port->c_dest;
|
||||
subscribers_t *subs;
|
||||
struct list_head *p;
|
||||
int err = -ENOENT;
|
||||
unsigned long flags;
|
||||
|
||||
down_write(&src->list_mutex);
|
||||
down_write(&dest->list_mutex);
|
||||
|
||||
/* look for the connection */
|
||||
list_for_each(p, &src->list_head) {
|
||||
subs = list_entry(p, subscribers_t, src_list);
|
||||
if (match_subs_info(info, &subs->info)) {
|
||||
write_lock_irqsave(&src->list_lock, flags);
|
||||
// write_lock(&dest->list_lock); // no lock yet
|
||||
list_del(&subs->src_list);
|
||||
list_del(&subs->dest_list);
|
||||
// write_unlock(&dest->list_lock);
|
||||
write_unlock_irqrestore(&src->list_lock, flags);
|
||||
src->exclusive = dest->exclusive = 0;
|
||||
unsubscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number);
|
||||
unsubscribe_port(dest_client, dest_port, dest, info,
|
||||
connector->number != dest_client->number);
|
||||
kfree(subs);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* get matched subscriber */
|
||||
subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp,
|
||||
snd_seq_addr_t *dest_addr)
|
||||
{
|
||||
struct list_head *p;
|
||||
subscribers_t *s, *found = NULL;
|
||||
|
||||
down_read(&src_grp->list_mutex);
|
||||
list_for_each(p, &src_grp->list_head) {
|
||||
s = list_entry(p, subscribers_t, src_list);
|
||||
if (addr_match(dest_addr, &s->info.dest)) {
|
||||
found = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up_read(&src_grp->list_mutex);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach a device driver that wants to receive events from the
|
||||
* sequencer. Returns the new port number on success.
|
||||
* A driver that wants to receive the events converted to midi, will
|
||||
* use snd_seq_midisynth_register_port().
|
||||
*/
|
||||
/* exported */
|
||||
int snd_seq_event_port_attach(int client,
|
||||
snd_seq_port_callback_t *pcbp,
|
||||
int cap, int type, int midi_channels,
|
||||
int midi_voices, char *portname)
|
||||
{
|
||||
snd_seq_port_info_t portinfo;
|
||||
int ret;
|
||||
|
||||
/* Set up the port */
|
||||
memset(&portinfo, 0, sizeof(portinfo));
|
||||
portinfo.addr.client = client;
|
||||
strlcpy(portinfo.name, portname ? portname : "Unamed port",
|
||||
sizeof(portinfo.name));
|
||||
|
||||
portinfo.capability = cap;
|
||||
portinfo.type = type;
|
||||
portinfo.kernel = pcbp;
|
||||
portinfo.midi_channels = midi_channels;
|
||||
portinfo.midi_voices = midi_voices;
|
||||
|
||||
/* Create it */
|
||||
ret = snd_seq_kernel_client_ctl(client,
|
||||
SNDRV_SEQ_IOCTL_CREATE_PORT,
|
||||
&portinfo);
|
||||
|
||||
if (ret >= 0)
|
||||
ret = portinfo.addr.port;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Detach the driver from a port.
|
||||
*/
|
||||
/* exported */
|
||||
int snd_seq_event_port_detach(int client, int port)
|
||||
{
|
||||
snd_seq_port_info_t portinfo;
|
||||
int err;
|
||||
|
||||
memset(&portinfo, 0, sizeof(portinfo));
|
||||
portinfo.addr.client = client;
|
||||
portinfo.addr.port = port;
|
||||
err = snd_seq_kernel_client_ctl(client,
|
||||
SNDRV_SEQ_IOCTL_DELETE_PORT,
|
||||
&portinfo);
|
||||
|
||||
return err;
|
||||
}
|
||||
128
sound/core/seq/seq_ports.h
Normal file
128
sound/core/seq/seq_ports.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* ALSA sequencer Ports
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_PORTS_H
|
||||
#define __SND_SEQ_PORTS_H
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
#include "seq_lock.h"
|
||||
|
||||
/* list of 'exported' ports */
|
||||
|
||||
/* Client ports that are not exported are still accessible, but are
|
||||
anonymous ports.
|
||||
|
||||
If a port supports SUBSCRIPTION, that port can send events to all
|
||||
subscribersto a special address, with address
|
||||
(queue==SNDRV_SEQ_ADDRESS_SUBSCRIBERS). The message is then send to all
|
||||
recipients that are registered in the subscription list. A typical
|
||||
application for these SUBSCRIPTION events is handling of incoming MIDI
|
||||
data. The port doesn't 'know' what other clients are interested in this
|
||||
message. If for instance a MIDI recording application would like to receive
|
||||
the events from that port, it will first have to subscribe with that port.
|
||||
|
||||
*/
|
||||
|
||||
typedef struct subscribers_t {
|
||||
snd_seq_port_subscribe_t info; /* additional info */
|
||||
struct list_head src_list; /* link of sources */
|
||||
struct list_head dest_list; /* link of destinations */
|
||||
atomic_t ref_count;
|
||||
} subscribers_t;
|
||||
|
||||
typedef struct port_subs_info_t {
|
||||
struct list_head list_head; /* list of subscribed ports */
|
||||
unsigned int count; /* count of subscribers */
|
||||
unsigned int exclusive: 1; /* exclusive mode */
|
||||
struct rw_semaphore list_mutex;
|
||||
rwlock_t list_lock;
|
||||
snd_seq_kernel_port_open_t *open;
|
||||
snd_seq_kernel_port_close_t *close;
|
||||
} port_subs_info_t;
|
||||
|
||||
typedef struct client_port_t {
|
||||
|
||||
snd_seq_addr_t addr; /* client/port number */
|
||||
struct module *owner; /* owner of this port */
|
||||
char name[64]; /* port name */
|
||||
struct list_head list; /* port list */
|
||||
snd_use_lock_t use_lock;
|
||||
|
||||
/* subscribers */
|
||||
port_subs_info_t c_src; /* read (sender) list */
|
||||
port_subs_info_t c_dest; /* write (dest) list */
|
||||
|
||||
snd_seq_kernel_port_input_t *event_input;
|
||||
snd_seq_kernel_port_private_free_t *private_free;
|
||||
void *private_data;
|
||||
unsigned int callback_all : 1;
|
||||
unsigned int closing : 1;
|
||||
unsigned int timestamping: 1;
|
||||
unsigned int time_real: 1;
|
||||
int time_queue;
|
||||
|
||||
/* capability, inport, output, sync */
|
||||
unsigned int capability; /* port capability bits */
|
||||
unsigned int type; /* port type bits */
|
||||
|
||||
/* supported channels */
|
||||
int midi_channels;
|
||||
int midi_voices;
|
||||
int synth_voices;
|
||||
|
||||
} client_port_t;
|
||||
|
||||
/* return pointer to port structure and lock port */
|
||||
client_port_t *snd_seq_port_use_ptr(client_t *client, int num);
|
||||
|
||||
/* search for next port - port is locked if found */
|
||||
client_port_t *snd_seq_port_query_nearest(client_t *client, snd_seq_port_info_t *pinfo);
|
||||
|
||||
/* unlock the port */
|
||||
#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
|
||||
|
||||
/* create a port, port number is returned (-1 on failure) */
|
||||
client_port_t *snd_seq_create_port(client_t *client, int port_index);
|
||||
|
||||
/* delete a port */
|
||||
int snd_seq_delete_port(client_t *client, int port);
|
||||
|
||||
/* delete all ports */
|
||||
int snd_seq_delete_all_ports(client_t *client);
|
||||
|
||||
/* set port info fields */
|
||||
int snd_seq_set_port_info(client_port_t *port, snd_seq_port_info_t *info);
|
||||
|
||||
/* get port info fields */
|
||||
int snd_seq_get_port_info(client_port_t *port, snd_seq_port_info_t *info);
|
||||
|
||||
/* add subscriber to subscription list */
|
||||
int snd_seq_port_connect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
|
||||
|
||||
/* remove subscriber from subscription list */
|
||||
int snd_seq_port_disconnect(client_t *caller, client_t *s, client_port_t *sp, client_t *d, client_port_t *dp, snd_seq_port_subscribe_t *info);
|
||||
|
||||
/* subscribe port */
|
||||
int snd_seq_port_subscribe(client_port_t *port, snd_seq_port_subscribe_t *info);
|
||||
|
||||
/* get matched subscriber */
|
||||
subscribers_t *snd_seq_port_get_subscription(port_subs_info_t *src_grp, snd_seq_addr_t *dest_addr);
|
||||
|
||||
#endif
|
||||
449
sound/core/seq/seq_prioq.c
Normal file
449
sound/core/seq/seq_prioq.c
Normal file
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* ALSA sequencer Priority Queue
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include "seq_timer.h"
|
||||
#include "seq_prioq.h"
|
||||
|
||||
|
||||
/* Implementation is a simple linked list for now...
|
||||
|
||||
This priority queue orders the events on timestamp. For events with an
|
||||
equeal timestamp the queue behaves as a FIFO.
|
||||
|
||||
*
|
||||
* +-------+
|
||||
* Head --> | first |
|
||||
* +-------+
|
||||
* |next
|
||||
* +-----v-+
|
||||
* | |
|
||||
* +-------+
|
||||
* |
|
||||
* +-----v-+
|
||||
* | |
|
||||
* +-------+
|
||||
* |
|
||||
* +-----v-+
|
||||
* Tail --> | last |
|
||||
* +-------+
|
||||
*
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* create new prioq (constructor) */
|
||||
prioq_t *snd_seq_prioq_new(void)
|
||||
{
|
||||
prioq_t *f;
|
||||
|
||||
f = kcalloc(1, sizeof(*f), GFP_KERNEL);
|
||||
if (f == NULL) {
|
||||
snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_init(&f->lock);
|
||||
f->head = NULL;
|
||||
f->tail = NULL;
|
||||
f->cells = 0;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* delete prioq (destructor) */
|
||||
void snd_seq_prioq_delete(prioq_t **fifo)
|
||||
{
|
||||
prioq_t *f = *fifo;
|
||||
*fifo = NULL;
|
||||
|
||||
if (f == NULL) {
|
||||
snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* release resources...*/
|
||||
/*....................*/
|
||||
|
||||
if (f->cells > 0) {
|
||||
/* drain prioQ */
|
||||
while (f->cells > 0)
|
||||
snd_seq_cell_free(snd_seq_prioq_cell_out(f));
|
||||
}
|
||||
|
||||
kfree(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* compare timestamp between events */
|
||||
/* return 1 if a >= b; 0 */
|
||||
static inline int compare_timestamp(snd_seq_event_t * a, snd_seq_event_t * b)
|
||||
{
|
||||
if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
|
||||
/* compare ticks */
|
||||
return (snd_seq_compare_tick_time(&a->time.tick, &b->time.tick));
|
||||
} else {
|
||||
/* compare real time */
|
||||
return (snd_seq_compare_real_time(&a->time.time, &b->time.time));
|
||||
}
|
||||
}
|
||||
|
||||
/* compare timestamp between events */
|
||||
/* return negative if a < b;
|
||||
* zero if a = b;
|
||||
* positive if a > b;
|
||||
*/
|
||||
static inline int compare_timestamp_rel(snd_seq_event_t *a, snd_seq_event_t *b)
|
||||
{
|
||||
if ((a->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) {
|
||||
/* compare ticks */
|
||||
if (a->time.tick > b->time.tick)
|
||||
return 1;
|
||||
else if (a->time.tick == b->time.tick)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
} else {
|
||||
/* compare real time */
|
||||
if (a->time.time.tv_sec > b->time.time.tv_sec)
|
||||
return 1;
|
||||
else if (a->time.time.tv_sec == b->time.time.tv_sec) {
|
||||
if (a->time.time.tv_nsec > b->time.time.tv_nsec)
|
||||
return 1;
|
||||
else if (a->time.time.tv_nsec == b->time.time.tv_nsec)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* enqueue cell to prioq */
|
||||
int snd_seq_prioq_cell_in(prioq_t * f, snd_seq_event_cell_t * cell)
|
||||
{
|
||||
snd_seq_event_cell_t *cur, *prev;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
int prior;
|
||||
|
||||
snd_assert(f, return -EINVAL);
|
||||
snd_assert(cell, return -EINVAL);
|
||||
|
||||
/* check flags */
|
||||
prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
|
||||
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
|
||||
/* check if this element needs to inserted at the end (ie. ordered
|
||||
data is inserted) This will be very likeley if a sequencer
|
||||
application or midi file player is feeding us (sequential) data */
|
||||
if (f->tail && !prior) {
|
||||
if (compare_timestamp(&cell->event, &f->tail->event)) {
|
||||
/* add new cell to tail of the fifo */
|
||||
f->tail->next = cell;
|
||||
f->tail = cell;
|
||||
cell->next = NULL;
|
||||
f->cells++;
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* traverse list of elements to find the place where the new cell is
|
||||
to be inserted... Note that this is a order n process ! */
|
||||
|
||||
prev = NULL; /* previous cell */
|
||||
cur = f->head; /* cursor */
|
||||
|
||||
count = 10000; /* FIXME: enough big, isn't it? */
|
||||
while (cur != NULL) {
|
||||
/* compare timestamps */
|
||||
int rel = compare_timestamp_rel(&cell->event, &cur->event);
|
||||
if (rel < 0)
|
||||
/* new cell has earlier schedule time, */
|
||||
break;
|
||||
else if (rel == 0 && prior)
|
||||
/* equal schedule time and prior to others */
|
||||
break;
|
||||
/* new cell has equal or larger schedule time, */
|
||||
/* move cursor to next cell */
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
if (! --count) {
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* insert it before cursor */
|
||||
if (prev != NULL)
|
||||
prev->next = cell;
|
||||
cell->next = cur;
|
||||
|
||||
if (f->head == cur) /* this is the first cell, set head to it */
|
||||
f->head = cell;
|
||||
if (cur == NULL) /* reached end of the list */
|
||||
f->tail = cell;
|
||||
f->cells++;
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dequeue cell from prioq */
|
||||
snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t * f)
|
||||
{
|
||||
snd_seq_event_cell_t *cell;
|
||||
unsigned long flags;
|
||||
|
||||
if (f == NULL) {
|
||||
snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
|
||||
return NULL;
|
||||
}
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
|
||||
cell = f->head;
|
||||
if (cell) {
|
||||
f->head = cell->next;
|
||||
|
||||
/* reset tail if this was the last element */
|
||||
if (f->tail == cell)
|
||||
f->tail = NULL;
|
||||
|
||||
cell->next = NULL;
|
||||
f->cells--;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
return cell;
|
||||
}
|
||||
|
||||
/* return number of events available in prioq */
|
||||
int snd_seq_prioq_avail(prioq_t * f)
|
||||
{
|
||||
if (f == NULL) {
|
||||
snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
|
||||
return 0;
|
||||
}
|
||||
return f->cells;
|
||||
}
|
||||
|
||||
|
||||
/* peek at cell at the head of the prioq */
|
||||
snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t * f)
|
||||
{
|
||||
if (f == NULL) {
|
||||
snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
|
||||
return NULL;
|
||||
}
|
||||
return f->head;
|
||||
}
|
||||
|
||||
|
||||
static inline int prioq_match(snd_seq_event_cell_t *cell, int client, int timestamp)
|
||||
{
|
||||
if (cell->event.source.client == client ||
|
||||
cell->event.dest.client == client)
|
||||
return 1;
|
||||
if (!timestamp)
|
||||
return 0;
|
||||
switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
|
||||
case SNDRV_SEQ_TIME_STAMP_TICK:
|
||||
if (cell->event.time.tick)
|
||||
return 1;
|
||||
break;
|
||||
case SNDRV_SEQ_TIME_STAMP_REAL:
|
||||
if (cell->event.time.time.tv_sec ||
|
||||
cell->event.time.time.tv_nsec)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove cells for left client */
|
||||
void snd_seq_prioq_leave(prioq_t * f, int client, int timestamp)
|
||||
{
|
||||
register snd_seq_event_cell_t *cell, *next;
|
||||
unsigned long flags;
|
||||
snd_seq_event_cell_t *prev = NULL;
|
||||
snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
|
||||
|
||||
/* collect all removed cells */
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
cell = f->head;
|
||||
while (cell) {
|
||||
next = cell->next;
|
||||
if (prioq_match(cell, client, timestamp)) {
|
||||
/* remove cell from prioq */
|
||||
if (cell == f->head) {
|
||||
f->head = cell->next;
|
||||
} else {
|
||||
prev->next = cell->next;
|
||||
}
|
||||
if (cell == f->tail)
|
||||
f->tail = cell->next;
|
||||
f->cells--;
|
||||
/* add cell to free list */
|
||||
cell->next = NULL;
|
||||
if (freefirst == NULL) {
|
||||
freefirst = cell;
|
||||
} else {
|
||||
freeprev->next = cell;
|
||||
}
|
||||
freeprev = cell;
|
||||
} else {
|
||||
#if 0
|
||||
printk("type = %i, source = %i, dest = %i, client = %i\n",
|
||||
cell->event.type,
|
||||
cell->event.source.client,
|
||||
cell->event.dest.client,
|
||||
client);
|
||||
#endif
|
||||
prev = cell;
|
||||
}
|
||||
cell = next;
|
||||
}
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
|
||||
/* remove selected cells */
|
||||
while (freefirst) {
|
||||
freenext = freefirst->next;
|
||||
snd_seq_cell_free(freefirst);
|
||||
freefirst = freenext;
|
||||
}
|
||||
}
|
||||
|
||||
static int prioq_remove_match(snd_seq_remove_events_t *info,
|
||||
snd_seq_event_t *ev)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
|
||||
if (ev->dest.client != info->dest.client ||
|
||||
ev->dest.port != info->dest.port)
|
||||
return 0;
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
|
||||
if (! snd_seq_ev_is_channel_type(ev))
|
||||
return 0;
|
||||
/* data.note.channel and data.control.channel are identical */
|
||||
if (ev->data.note.channel != info->channel)
|
||||
return 0;
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
|
||||
res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
|
||||
else
|
||||
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
|
||||
if (!res)
|
||||
return 0;
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
|
||||
res = snd_seq_compare_tick_time(&ev->time.tick, &info->time.tick);
|
||||
else
|
||||
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
|
||||
if (res)
|
||||
return 0;
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
|
||||
if (ev->type != info->type)
|
||||
return 0;
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
|
||||
/* Do not remove off events */
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_NOTEOFF:
|
||||
/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
|
||||
if (info->tag != ev->tag)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* remove cells matching remove criteria */
|
||||
void snd_seq_prioq_remove_events(prioq_t * f, int client,
|
||||
snd_seq_remove_events_t *info)
|
||||
{
|
||||
register snd_seq_event_cell_t *cell, *next;
|
||||
unsigned long flags;
|
||||
snd_seq_event_cell_t *prev = NULL;
|
||||
snd_seq_event_cell_t *freefirst = NULL, *freeprev = NULL, *freenext;
|
||||
|
||||
/* collect all removed cells */
|
||||
spin_lock_irqsave(&f->lock, flags);
|
||||
cell = f->head;
|
||||
|
||||
while (cell) {
|
||||
next = cell->next;
|
||||
if (cell->event.source.client == client &&
|
||||
prioq_remove_match(info, &cell->event)) {
|
||||
|
||||
/* remove cell from prioq */
|
||||
if (cell == f->head) {
|
||||
f->head = cell->next;
|
||||
} else {
|
||||
prev->next = cell->next;
|
||||
}
|
||||
|
||||
if (cell == f->tail)
|
||||
f->tail = cell->next;
|
||||
f->cells--;
|
||||
|
||||
/* add cell to free list */
|
||||
cell->next = NULL;
|
||||
if (freefirst == NULL) {
|
||||
freefirst = cell;
|
||||
} else {
|
||||
freeprev->next = cell;
|
||||
}
|
||||
|
||||
freeprev = cell;
|
||||
} else {
|
||||
prev = cell;
|
||||
}
|
||||
cell = next;
|
||||
}
|
||||
spin_unlock_irqrestore(&f->lock, flags);
|
||||
|
||||
/* remove selected cells */
|
||||
while (freefirst) {
|
||||
freenext = freefirst->next;
|
||||
snd_seq_cell_free(freefirst);
|
||||
freefirst = freenext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
62
sound/core/seq/seq_prioq.h
Normal file
62
sound/core/seq/seq_prioq.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* ALSA sequencer Priority Queue
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_PRIOQ_H
|
||||
#define __SND_SEQ_PRIOQ_H
|
||||
|
||||
#include "seq_memory.h"
|
||||
|
||||
|
||||
/* === PRIOQ === */
|
||||
|
||||
typedef struct {
|
||||
snd_seq_event_cell_t* head; /* pointer to head of prioq */
|
||||
snd_seq_event_cell_t* tail; /* pointer to tail of prioq */
|
||||
int cells;
|
||||
spinlock_t lock;
|
||||
} prioq_t;
|
||||
|
||||
|
||||
/* create new prioq (constructor) */
|
||||
extern prioq_t *snd_seq_prioq_new(void);
|
||||
|
||||
/* delete prioq (destructor) */
|
||||
extern void snd_seq_prioq_delete(prioq_t **fifo);
|
||||
|
||||
/* enqueue cell to prioq */
|
||||
extern int snd_seq_prioq_cell_in(prioq_t *f, snd_seq_event_cell_t *cell);
|
||||
|
||||
/* dequeue cell from prioq */
|
||||
extern snd_seq_event_cell_t *snd_seq_prioq_cell_out(prioq_t *f);
|
||||
|
||||
/* return number of events available in prioq */
|
||||
extern int snd_seq_prioq_avail(prioq_t *f);
|
||||
|
||||
/* peek at cell at the head of the prioq */
|
||||
extern snd_seq_event_cell_t *snd_seq_prioq_cell_peek(prioq_t *f);
|
||||
|
||||
/* client left queue */
|
||||
extern void snd_seq_prioq_leave(prioq_t *f, int client, int timestamp);
|
||||
|
||||
/* Remove events */
|
||||
void snd_seq_prioq_remove_events(prioq_t * f, int client,
|
||||
snd_seq_remove_events_t *info);
|
||||
|
||||
#endif
|
||||
783
sound/core/seq/seq_queue.c
Normal file
783
sound/core/seq/seq_queue.c
Normal file
@@ -0,0 +1,783 @@
|
||||
/*
|
||||
* ALSA sequencer Timing queue handling
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* MAJOR CHANGES
|
||||
* Nov. 13, 1999 Takashi Iwai <iwai@ww.uni-erlangen.de>
|
||||
* - Queues are allocated dynamically via ioctl.
|
||||
* - When owner client is deleted, all owned queues are deleted, too.
|
||||
* - Owner of unlocked queue is kept unmodified even if it is
|
||||
* manipulated by other clients.
|
||||
* - Owner field in SET_QUEUE_OWNER ioctl must be identical with the
|
||||
* caller client. i.e. Changing owner to a third client is not
|
||||
* allowed.
|
||||
*
|
||||
* Aug. 30, 2000 Takashi Iwai
|
||||
* - Queues are managed in static array again, but with better way.
|
||||
* The API itself is identical.
|
||||
* - The queue is locked when queue_t pinter is returned via
|
||||
* queueptr(). This pointer *MUST* be released afterward by
|
||||
* queuefree(ptr).
|
||||
* - Addition of experimental sync support.
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "seq_memory.h"
|
||||
#include "seq_queue.h"
|
||||
#include "seq_clientmgr.h"
|
||||
#include "seq_fifo.h"
|
||||
#include "seq_timer.h"
|
||||
#include "seq_info.h"
|
||||
|
||||
/* list of allocated queues */
|
||||
static queue_t *queue_list[SNDRV_SEQ_MAX_QUEUES];
|
||||
static DEFINE_SPINLOCK(queue_list_lock);
|
||||
/* number of queues allocated */
|
||||
static int num_queues;
|
||||
|
||||
int snd_seq_queue_get_cur_queues(void)
|
||||
{
|
||||
return num_queues;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* assign queue id and insert to list */
|
||||
static int queue_list_add(queue_t *q)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&queue_list_lock, flags);
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if (! queue_list[i]) {
|
||||
queue_list[i] = q;
|
||||
q->queue = i;
|
||||
num_queues++;
|
||||
spin_unlock_irqrestore(&queue_list_lock, flags);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&queue_list_lock, flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static queue_t *queue_list_remove(int id, int client)
|
||||
{
|
||||
queue_t *q;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&queue_list_lock, flags);
|
||||
q = queue_list[id];
|
||||
if (q) {
|
||||
spin_lock(&q->owner_lock);
|
||||
if (q->owner == client) {
|
||||
/* found */
|
||||
q->klocked = 1;
|
||||
spin_unlock(&q->owner_lock);
|
||||
queue_list[id] = NULL;
|
||||
num_queues--;
|
||||
spin_unlock_irqrestore(&queue_list_lock, flags);
|
||||
return q;
|
||||
}
|
||||
spin_unlock(&q->owner_lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&queue_list_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* create new queue (constructor) */
|
||||
static queue_t *queue_new(int owner, int locked)
|
||||
{
|
||||
queue_t *q;
|
||||
|
||||
q = kcalloc(1, sizeof(*q), GFP_KERNEL);
|
||||
if (q == NULL) {
|
||||
snd_printd("malloc failed for snd_seq_queue_new()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_init(&q->owner_lock);
|
||||
spin_lock_init(&q->check_lock);
|
||||
init_MUTEX(&q->timer_mutex);
|
||||
snd_use_lock_init(&q->use_lock);
|
||||
q->queue = -1;
|
||||
|
||||
q->tickq = snd_seq_prioq_new();
|
||||
q->timeq = snd_seq_prioq_new();
|
||||
q->timer = snd_seq_timer_new();
|
||||
if (q->tickq == NULL || q->timeq == NULL || q->timer == NULL) {
|
||||
snd_seq_prioq_delete(&q->tickq);
|
||||
snd_seq_prioq_delete(&q->timeq);
|
||||
snd_seq_timer_delete(&q->timer);
|
||||
kfree(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
q->owner = owner;
|
||||
q->locked = locked;
|
||||
q->klocked = 0;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/* delete queue (destructor) */
|
||||
static void queue_delete(queue_t *q)
|
||||
{
|
||||
/* stop and release the timer */
|
||||
snd_seq_timer_stop(q->timer);
|
||||
snd_seq_timer_close(q);
|
||||
/* wait until access free */
|
||||
snd_use_lock_sync(&q->use_lock);
|
||||
/* release resources... */
|
||||
snd_seq_prioq_delete(&q->tickq);
|
||||
snd_seq_prioq_delete(&q->timeq);
|
||||
snd_seq_timer_delete(&q->timer);
|
||||
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* setup queues */
|
||||
int __init snd_seq_queues_init(void)
|
||||
{
|
||||
/*
|
||||
memset(queue_list, 0, sizeof(queue_list));
|
||||
num_queues = 0;
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* delete all existing queues */
|
||||
void __exit snd_seq_queues_delete(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* clear list */
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if (queue_list[i])
|
||||
queue_delete(queue_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate a new queue -
|
||||
* return queue index value or negative value for error
|
||||
*/
|
||||
int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
|
||||
{
|
||||
queue_t *q;
|
||||
|
||||
q = queue_new(client, locked);
|
||||
if (q == NULL)
|
||||
return -ENOMEM;
|
||||
q->info_flags = info_flags;
|
||||
if (queue_list_add(q) < 0) {
|
||||
queue_delete(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_seq_queue_use(q->queue, client, 1); /* use this queue */
|
||||
return q->queue;
|
||||
}
|
||||
|
||||
/* delete a queue - queue must be owned by the client */
|
||||
int snd_seq_queue_delete(int client, int queueid)
|
||||
{
|
||||
queue_t *q;
|
||||
|
||||
if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
|
||||
return -EINVAL;
|
||||
q = queue_list_remove(queueid, client);
|
||||
if (q == NULL)
|
||||
return -EINVAL;
|
||||
queue_delete(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* return pointer to queue structure for specified id */
|
||||
queue_t *queueptr(int queueid)
|
||||
{
|
||||
queue_t *q;
|
||||
unsigned long flags;
|
||||
|
||||
if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
|
||||
return NULL;
|
||||
spin_lock_irqsave(&queue_list_lock, flags);
|
||||
q = queue_list[queueid];
|
||||
if (q)
|
||||
snd_use_lock_use(&q->use_lock);
|
||||
spin_unlock_irqrestore(&queue_list_lock, flags);
|
||||
return q;
|
||||
}
|
||||
|
||||
/* return the (first) queue matching with the specified name */
|
||||
queue_t *snd_seq_queue_find_name(char *name)
|
||||
{
|
||||
int i;
|
||||
queue_t *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) != NULL) {
|
||||
if (strncmp(q->name, name, sizeof(q->name)) == 0)
|
||||
return q;
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
void snd_seq_check_queue(queue_t *q, int atomic, int hop)
|
||||
{
|
||||
unsigned long flags;
|
||||
snd_seq_event_cell_t *cell;
|
||||
|
||||
if (q == NULL)
|
||||
return;
|
||||
|
||||
/* make this function non-reentrant */
|
||||
spin_lock_irqsave(&q->check_lock, flags);
|
||||
if (q->check_blocked) {
|
||||
q->check_again = 1;
|
||||
spin_unlock_irqrestore(&q->check_lock, flags);
|
||||
return; /* other thread is already checking queues */
|
||||
}
|
||||
q->check_blocked = 1;
|
||||
spin_unlock_irqrestore(&q->check_lock, flags);
|
||||
|
||||
__again:
|
||||
/* Process tick queue... */
|
||||
while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
|
||||
if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, &cell->event.time.tick)) {
|
||||
cell = snd_seq_prioq_cell_out(q->tickq);
|
||||
if (cell)
|
||||
snd_seq_dispatch_event(cell, atomic, hop);
|
||||
} else {
|
||||
/* event remains in the queue */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Process time queue... */
|
||||
while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
|
||||
if (snd_seq_compare_real_time(&q->timer->cur_time, &cell->event.time.time)) {
|
||||
cell = snd_seq_prioq_cell_out(q->timeq);
|
||||
if (cell)
|
||||
snd_seq_dispatch_event(cell, atomic, hop);
|
||||
} else {
|
||||
/* event remains in the queue */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* free lock */
|
||||
spin_lock_irqsave(&q->check_lock, flags);
|
||||
if (q->check_again) {
|
||||
q->check_again = 0;
|
||||
spin_unlock_irqrestore(&q->check_lock, flags);
|
||||
goto __again;
|
||||
}
|
||||
q->check_blocked = 0;
|
||||
spin_unlock_irqrestore(&q->check_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/* enqueue a event to singe queue */
|
||||
int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop)
|
||||
{
|
||||
int dest, err;
|
||||
queue_t *q;
|
||||
|
||||
snd_assert(cell != NULL, return -EINVAL);
|
||||
dest = cell->event.queue; /* destination queue */
|
||||
q = queueptr(dest);
|
||||
if (q == NULL)
|
||||
return -EINVAL;
|
||||
/* handle relative time stamps, convert them into absolute */
|
||||
if ((cell->event.flags & SNDRV_SEQ_TIME_MODE_MASK) == SNDRV_SEQ_TIME_MODE_REL) {
|
||||
switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
|
||||
case SNDRV_SEQ_TIME_STAMP_TICK:
|
||||
cell->event.time.tick += q->timer->tick.cur_tick;
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_TIME_STAMP_REAL:
|
||||
snd_seq_inc_real_time(&cell->event.time.time, &q->timer->cur_time);
|
||||
break;
|
||||
}
|
||||
cell->event.flags &= ~SNDRV_SEQ_TIME_MODE_MASK;
|
||||
cell->event.flags |= SNDRV_SEQ_TIME_MODE_ABS;
|
||||
}
|
||||
/* enqueue event in the real-time or midi queue */
|
||||
switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
|
||||
case SNDRV_SEQ_TIME_STAMP_TICK:
|
||||
err = snd_seq_prioq_cell_in(q->tickq, cell);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_TIME_STAMP_REAL:
|
||||
default:
|
||||
err = snd_seq_prioq_cell_in(q->timeq, cell);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
queuefree(q); /* unlock */
|
||||
return err;
|
||||
}
|
||||
|
||||
/* trigger dispatching */
|
||||
snd_seq_check_queue(q, atomic, hop);
|
||||
|
||||
queuefree(q); /* unlock */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
static inline int check_access(queue_t *q, int client)
|
||||
{
|
||||
return (q->owner == client) || (!q->locked && !q->klocked);
|
||||
}
|
||||
|
||||
/* check if the client has permission to modify queue parameters.
|
||||
* if it does, lock the queue
|
||||
*/
|
||||
static int queue_access_lock(queue_t *q, int client)
|
||||
{
|
||||
unsigned long flags;
|
||||
int access_ok;
|
||||
|
||||
spin_lock_irqsave(&q->owner_lock, flags);
|
||||
access_ok = check_access(q, client);
|
||||
if (access_ok)
|
||||
q->klocked = 1;
|
||||
spin_unlock_irqrestore(&q->owner_lock, flags);
|
||||
return access_ok;
|
||||
}
|
||||
|
||||
/* unlock the queue */
|
||||
static inline void queue_access_unlock(queue_t *q)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&q->owner_lock, flags);
|
||||
q->klocked = 0;
|
||||
spin_unlock_irqrestore(&q->owner_lock, flags);
|
||||
}
|
||||
|
||||
/* exported - only checking permission */
|
||||
int snd_seq_queue_check_access(int queueid, int client)
|
||||
{
|
||||
queue_t *q = queueptr(queueid);
|
||||
int access_ok;
|
||||
unsigned long flags;
|
||||
|
||||
if (! q)
|
||||
return 0;
|
||||
spin_lock_irqsave(&q->owner_lock, flags);
|
||||
access_ok = check_access(q, client);
|
||||
spin_unlock_irqrestore(&q->owner_lock, flags);
|
||||
queuefree(q);
|
||||
return access_ok;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* change queue's owner and permission
|
||||
*/
|
||||
int snd_seq_queue_set_owner(int queueid, int client, int locked)
|
||||
{
|
||||
queue_t *q = queueptr(queueid);
|
||||
|
||||
if (q == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (! queue_access_lock(q, client)) {
|
||||
queuefree(q);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
q->locked = locked ? 1 : 0;
|
||||
q->owner = client;
|
||||
queue_access_unlock(q);
|
||||
queuefree(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* open timer -
|
||||
* q->use mutex should be down before calling this function to avoid
|
||||
* confliction with snd_seq_queue_use()
|
||||
*/
|
||||
int snd_seq_queue_timer_open(int queueid)
|
||||
{
|
||||
int result = 0;
|
||||
queue_t *queue;
|
||||
seq_timer_t *tmr;
|
||||
|
||||
queue = queueptr(queueid);
|
||||
if (queue == NULL)
|
||||
return -EINVAL;
|
||||
tmr = queue->timer;
|
||||
if ((result = snd_seq_timer_open(queue)) < 0) {
|
||||
snd_seq_timer_defaults(tmr);
|
||||
result = snd_seq_timer_open(queue);
|
||||
}
|
||||
queuefree(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* close timer -
|
||||
* q->use mutex should be down before calling this function
|
||||
*/
|
||||
int snd_seq_queue_timer_close(int queueid)
|
||||
{
|
||||
queue_t *queue;
|
||||
seq_timer_t *tmr;
|
||||
int result = 0;
|
||||
|
||||
queue = queueptr(queueid);
|
||||
if (queue == NULL)
|
||||
return -EINVAL;
|
||||
tmr = queue->timer;
|
||||
snd_seq_timer_close(queue);
|
||||
queuefree(queue);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* change queue tempo and ppq */
|
||||
int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info)
|
||||
{
|
||||
queue_t *q = queueptr(queueid);
|
||||
int result;
|
||||
|
||||
if (q == NULL)
|
||||
return -EINVAL;
|
||||
if (! queue_access_lock(q, client)) {
|
||||
queuefree(q);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
result = snd_seq_timer_set_tempo(q->timer, info->tempo);
|
||||
if (result >= 0)
|
||||
result = snd_seq_timer_set_ppq(q->timer, info->ppq);
|
||||
if (result >= 0 && info->skew_base > 0)
|
||||
result = snd_seq_timer_set_skew(q->timer, info->skew_value, info->skew_base);
|
||||
queue_access_unlock(q);
|
||||
queuefree(q);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* use or unuse this queue -
|
||||
* if it is the first client, starts the timer.
|
||||
* if it is not longer used by any clients, stop the timer.
|
||||
*/
|
||||
int snd_seq_queue_use(int queueid, int client, int use)
|
||||
{
|
||||
queue_t *queue;
|
||||
|
||||
queue = queueptr(queueid);
|
||||
if (queue == NULL)
|
||||
return -EINVAL;
|
||||
down(&queue->timer_mutex);
|
||||
if (use) {
|
||||
if (!test_and_set_bit(client, queue->clients_bitmap))
|
||||
queue->clients++;
|
||||
} else {
|
||||
if (test_and_clear_bit(client, queue->clients_bitmap))
|
||||
queue->clients--;
|
||||
}
|
||||
if (queue->clients) {
|
||||
if (use && queue->clients == 1)
|
||||
snd_seq_timer_defaults(queue->timer);
|
||||
snd_seq_timer_open(queue);
|
||||
} else {
|
||||
snd_seq_timer_close(queue);
|
||||
}
|
||||
up(&queue->timer_mutex);
|
||||
queuefree(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if queue is used by the client
|
||||
* return negative value if the queue is invalid.
|
||||
* return 0 if not used, 1 if used.
|
||||
*/
|
||||
int snd_seq_queue_is_used(int queueid, int client)
|
||||
{
|
||||
queue_t *q;
|
||||
int result;
|
||||
|
||||
q = queueptr(queueid);
|
||||
if (q == NULL)
|
||||
return -EINVAL; /* invalid queue */
|
||||
result = test_bit(client, q->clients_bitmap) ? 1 : 0;
|
||||
queuefree(q);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* notification that client has left the system -
|
||||
* stop the timer on all queues owned by this client
|
||||
*/
|
||||
void snd_seq_queue_client_termination(int client)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
queue_t *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
continue;
|
||||
spin_lock_irqsave(&q->owner_lock, flags);
|
||||
if (q->owner == client)
|
||||
q->klocked = 1;
|
||||
spin_unlock_irqrestore(&q->owner_lock, flags);
|
||||
if (q->owner == client) {
|
||||
if (q->timer->running)
|
||||
snd_seq_timer_stop(q->timer);
|
||||
snd_seq_timer_reset(q->timer);
|
||||
}
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
|
||||
/* final stage notification -
|
||||
* remove cells for no longer exist client (for non-owned queue)
|
||||
* or delete this queue (for owned queue)
|
||||
*/
|
||||
void snd_seq_queue_client_leave(int client)
|
||||
{
|
||||
int i;
|
||||
queue_t *q;
|
||||
|
||||
/* delete own queues from queue list */
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queue_list_remove(i, client)) != NULL)
|
||||
queue_delete(q);
|
||||
}
|
||||
|
||||
/* remove cells from existing queues -
|
||||
* they are not owned by this client
|
||||
*/
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
continue;
|
||||
if (test_bit(client, q->clients_bitmap)) {
|
||||
snd_seq_prioq_leave(q->tickq, client, 0);
|
||||
snd_seq_prioq_leave(q->timeq, client, 0);
|
||||
snd_seq_queue_use(q->queue, client, 0);
|
||||
}
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* remove cells from all queues */
|
||||
void snd_seq_queue_client_leave_cells(int client)
|
||||
{
|
||||
int i;
|
||||
queue_t *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
continue;
|
||||
snd_seq_prioq_leave(q->tickq, client, 0);
|
||||
snd_seq_prioq_leave(q->timeq, client, 0);
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
|
||||
/* remove cells based on flush criteria */
|
||||
void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info)
|
||||
{
|
||||
int i;
|
||||
queue_t *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
continue;
|
||||
if (test_bit(client, q->clients_bitmap) &&
|
||||
(! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
|
||||
q->queue == info->queue)) {
|
||||
snd_seq_prioq_remove_events(q->tickq, client, info);
|
||||
snd_seq_prioq_remove_events(q->timeq, client, info);
|
||||
}
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* send events to all subscribed ports
|
||||
*/
|
||||
static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
|
||||
{
|
||||
snd_seq_event_t sev;
|
||||
|
||||
sev = *ev;
|
||||
|
||||
sev.flags = SNDRV_SEQ_TIME_STAMP_TICK|SNDRV_SEQ_TIME_MODE_ABS;
|
||||
sev.time.tick = q->timer->tick.cur_tick;
|
||||
sev.queue = q->queue;
|
||||
sev.data.queue.queue = q->queue;
|
||||
|
||||
/* broadcast events from Timer port */
|
||||
sev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
|
||||
sev.source.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
|
||||
sev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
snd_seq_kernel_client_dispatch(SNDRV_SEQ_CLIENT_SYSTEM, &sev, atomic, hop);
|
||||
}
|
||||
|
||||
/*
|
||||
* process a received queue-control event.
|
||||
* this function is exported for seq_sync.c.
|
||||
*/
|
||||
void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
|
||||
{
|
||||
switch (ev->type) {
|
||||
case SNDRV_SEQ_EVENT_START:
|
||||
snd_seq_prioq_leave(q->tickq, ev->source.client, 1);
|
||||
snd_seq_prioq_leave(q->timeq, ev->source.client, 1);
|
||||
if (! snd_seq_timer_start(q->timer))
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_CONTINUE:
|
||||
if (! snd_seq_timer_continue(q->timer))
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_STOP:
|
||||
snd_seq_timer_stop(q->timer);
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_TEMPO:
|
||||
snd_seq_timer_set_tempo(q->timer, ev->data.queue.param.value);
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_SETPOS_TICK:
|
||||
if (snd_seq_timer_set_position_tick(q->timer, ev->data.queue.param.time.tick) == 0) {
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_SEQ_EVENT_SETPOS_TIME:
|
||||
if (snd_seq_timer_set_position_time(q->timer, ev->data.queue.param.time.time) == 0) {
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
}
|
||||
break;
|
||||
case SNDRV_SEQ_EVENT_QUEUE_SKEW:
|
||||
if (snd_seq_timer_set_skew(q->timer,
|
||||
ev->data.queue.param.skew.value,
|
||||
ev->data.queue.param.skew.base) == 0) {
|
||||
queue_broadcast_event(q, ev, atomic, hop);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Queue control via timer control port:
|
||||
* this function is exported as a callback of timer port.
|
||||
*/
|
||||
int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop)
|
||||
{
|
||||
queue_t *q;
|
||||
|
||||
snd_assert(ev != NULL, return -EINVAL);
|
||||
q = queueptr(ev->data.queue.queue);
|
||||
|
||||
if (q == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (! queue_access_lock(q, ev->source.client)) {
|
||||
queuefree(q);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
snd_seq_queue_process_event(q, ev, atomic, hop);
|
||||
|
||||
queue_access_unlock(q);
|
||||
queuefree(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
/* exported to seq_info.c */
|
||||
void snd_seq_info_queues_read(snd_info_entry_t *entry,
|
||||
snd_info_buffer_t * buffer)
|
||||
{
|
||||
int i, bpm;
|
||||
queue_t *q;
|
||||
seq_timer_t *tmr;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
continue;
|
||||
|
||||
tmr = q->timer;
|
||||
if (tmr->tempo)
|
||||
bpm = 60000000 / tmr->tempo;
|
||||
else
|
||||
bpm = 0;
|
||||
|
||||
snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
|
||||
snd_iprintf(buffer, "owned by client : %d\n", q->owner);
|
||||
snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
|
||||
snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
|
||||
snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
|
||||
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
|
||||
snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
|
||||
snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
|
||||
snd_iprintf(buffer, "current BPM : %d\n", bpm);
|
||||
snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
|
||||
snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
|
||||
snd_iprintf(buffer, "\n");
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
140
sound/core/seq/seq_queue.h
Normal file
140
sound/core/seq/seq_queue.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* ALSA sequencer Queue handling
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_QUEUE_H
|
||||
#define __SND_SEQ_QUEUE_H
|
||||
|
||||
#include "seq_memory.h"
|
||||
#include "seq_prioq.h"
|
||||
#include "seq_timer.h"
|
||||
#include "seq_lock.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define SEQ_QUEUE_NO_OWNER (-1)
|
||||
|
||||
struct _snd_seq_queue {
|
||||
int queue; /* queue number */
|
||||
|
||||
char name[64]; /* name of this queue */
|
||||
|
||||
prioq_t *tickq; /* midi tick event queue */
|
||||
prioq_t *timeq; /* real-time event queue */
|
||||
|
||||
seq_timer_t *timer; /* time keeper for this queue */
|
||||
int owner; /* client that 'owns' the timer */
|
||||
unsigned int locked:1, /* timer is only accesibble by owner if set */
|
||||
klocked:1, /* kernel lock (after START) */
|
||||
check_again:1,
|
||||
check_blocked:1;
|
||||
|
||||
unsigned int flags; /* status flags */
|
||||
unsigned int info_flags; /* info for sync */
|
||||
|
||||
spinlock_t owner_lock;
|
||||
spinlock_t check_lock;
|
||||
|
||||
/* clients which uses this queue (bitmap) */
|
||||
DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
|
||||
unsigned int clients; /* users of this queue */
|
||||
struct semaphore timer_mutex;
|
||||
|
||||
snd_use_lock_t use_lock;
|
||||
};
|
||||
|
||||
|
||||
/* get the number of current queues */
|
||||
int snd_seq_queue_get_cur_queues(void);
|
||||
|
||||
/* init queues structure */
|
||||
int snd_seq_queues_init(void);
|
||||
|
||||
/* delete queues */
|
||||
void snd_seq_queues_delete(void);
|
||||
|
||||
|
||||
/* create new queue (constructor) */
|
||||
int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
|
||||
|
||||
/* delete queue (destructor) */
|
||||
int snd_seq_queue_delete(int client, int queueid);
|
||||
|
||||
/* notification that client has left the system */
|
||||
void snd_seq_queue_client_termination(int client);
|
||||
|
||||
/* final stage */
|
||||
void snd_seq_queue_client_leave(int client);
|
||||
|
||||
/* enqueue a event received from one the clients */
|
||||
int snd_seq_enqueue_event(snd_seq_event_cell_t *cell, int atomic, int hop);
|
||||
|
||||
/* Remove events */
|
||||
void snd_seq_queue_client_leave_cells(int client);
|
||||
void snd_seq_queue_remove_cells(int client, snd_seq_remove_events_t *info);
|
||||
|
||||
/* return pointer to queue structure for specified id */
|
||||
queue_t *queueptr(int queueid);
|
||||
/* unlock */
|
||||
#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
|
||||
|
||||
/* return the (first) queue matching with the specified name */
|
||||
queue_t *snd_seq_queue_find_name(char *name);
|
||||
|
||||
/* check single queue and dispatch events */
|
||||
void snd_seq_check_queue(queue_t *q, int atomic, int hop);
|
||||
|
||||
/* access to queue's parameters */
|
||||
int snd_seq_queue_check_access(int queueid, int client);
|
||||
int snd_seq_queue_timer_set_tempo(int queueid, int client, snd_seq_queue_tempo_t *info);
|
||||
int snd_seq_queue_set_owner(int queueid, int client, int locked);
|
||||
int snd_seq_queue_set_locked(int queueid, int client, int locked);
|
||||
int snd_seq_queue_timer_open(int queueid);
|
||||
int snd_seq_queue_timer_close(int queueid);
|
||||
int snd_seq_queue_use(int queueid, int client, int use);
|
||||
int snd_seq_queue_is_used(int queueid, int client);
|
||||
|
||||
int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
|
||||
void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop);
|
||||
|
||||
/*
|
||||
* 64bit division - for sync stuff..
|
||||
*/
|
||||
#if defined(i386) || defined(i486)
|
||||
|
||||
#define udiv_qrnnd(q, r, n1, n0, d) \
|
||||
__asm__ ("divl %4" \
|
||||
: "=a" ((u32)(q)), \
|
||||
"=d" ((u32)(r)) \
|
||||
: "0" ((u32)(n0)), \
|
||||
"1" ((u32)(n1)), \
|
||||
"rm" ((u32)(d)))
|
||||
|
||||
#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
|
||||
#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
|
||||
#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
|
||||
|
||||
#else
|
||||
#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y)))
|
||||
#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y)))
|
||||
#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
190
sound/core/seq/seq_system.c
Normal file
190
sound/core/seq/seq_system.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* ALSA sequencer System services Client
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <sound/core.h>
|
||||
#include "seq_system.h"
|
||||
#include "seq_timer.h"
|
||||
#include "seq_queue.h"
|
||||
|
||||
/* internal client that provide system services, access to timer etc. */
|
||||
|
||||
/*
|
||||
* Port "Timer"
|
||||
* - send tempo /start/stop etc. events to this port to manipulate the
|
||||
* queue's timer. The queue address is specified in
|
||||
* data.queue.queue.
|
||||
* - this port supports subscription. The received timer events are
|
||||
* broadcasted to all subscribed clients. The modified tempo
|
||||
* value is stored on data.queue.value.
|
||||
* The modifier client/port is not send.
|
||||
*
|
||||
* Port "Announce"
|
||||
* - does not receive message
|
||||
* - supports supscription. For each client or port attaching to or
|
||||
* detaching from the system an announcement is send to the subscribed
|
||||
* clients.
|
||||
*
|
||||
* Idea: the subscription mechanism might also work handy for distributing
|
||||
* synchronisation and timing information. In this case we would ideally have
|
||||
* a list of subscribers for each type of sync (time, tick), for each timing
|
||||
* queue.
|
||||
*
|
||||
* NOTE: the queue to be started, stopped, etc. must be specified
|
||||
* in data.queue.addr.queue field. queue is used only for
|
||||
* scheduling, and no longer referred as affected queue.
|
||||
* They are used only for timer broadcast (see above).
|
||||
* -- iwai
|
||||
*/
|
||||
|
||||
|
||||
/* client id of our system client */
|
||||
static int sysclient = -1;
|
||||
|
||||
/* port id numbers for this client */
|
||||
static int announce_port = -1;
|
||||
|
||||
|
||||
|
||||
/* fill standard header data, source port & channel are filled in */
|
||||
static int setheader(snd_seq_event_t * ev, int client, int port)
|
||||
{
|
||||
if (announce_port < 0)
|
||||
return -ENODEV;
|
||||
|
||||
memset(ev, 0, sizeof(snd_seq_event_t));
|
||||
|
||||
ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
|
||||
|
||||
ev->source.client = sysclient;
|
||||
ev->source.port = announce_port;
|
||||
ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
|
||||
/* fill data */
|
||||
/*ev->data.addr.queue = SNDRV_SEQ_ADDRESS_UNKNOWN;*/
|
||||
ev->data.addr.client = client;
|
||||
ev->data.addr.port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
void snd_seq_system_broadcast(int client, int port, int type)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
if (setheader(&ev, client, port) < 0)
|
||||
return;
|
||||
ev.type = type;
|
||||
snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
|
||||
}
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev)
|
||||
{
|
||||
ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
|
||||
ev->source.client = sysclient;
|
||||
ev->source.port = announce_port;
|
||||
ev->dest.client = client;
|
||||
ev->dest.port = port;
|
||||
return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
|
||||
}
|
||||
|
||||
/* call-back handler for timer events */
|
||||
static int event_input_timer(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop)
|
||||
{
|
||||
return snd_seq_control_queue(ev, atomic, hop);
|
||||
}
|
||||
|
||||
/* register our internal client */
|
||||
int __init snd_seq_system_client_init(void)
|
||||
{
|
||||
|
||||
snd_seq_client_callback_t callbacks;
|
||||
snd_seq_port_callback_t pcallbacks;
|
||||
snd_seq_client_info_t *inf;
|
||||
snd_seq_port_info_t *port;
|
||||
|
||||
inf = kcalloc(1, sizeof(*inf), GFP_KERNEL);
|
||||
port = kcalloc(1, sizeof(*port), GFP_KERNEL);
|
||||
if (! inf || ! port) {
|
||||
kfree(inf);
|
||||
kfree(port);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
memset(&pcallbacks, 0, sizeof(pcallbacks));
|
||||
pcallbacks.owner = THIS_MODULE;
|
||||
pcallbacks.event_input = event_input_timer;
|
||||
|
||||
/* register client */
|
||||
callbacks.allow_input = callbacks.allow_output = 1;
|
||||
sysclient = snd_seq_create_kernel_client(NULL, 0, &callbacks);
|
||||
|
||||
/* set our name */
|
||||
inf->client = 0;
|
||||
inf->type = KERNEL_CLIENT;
|
||||
strcpy(inf->name, "System");
|
||||
snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, inf);
|
||||
|
||||
/* register timer */
|
||||
strcpy(port->name, "Timer");
|
||||
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
|
||||
port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
|
||||
port->kernel = &pcallbacks;
|
||||
port->type = 0;
|
||||
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
|
||||
port->addr.client = sysclient;
|
||||
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
|
||||
snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
|
||||
|
||||
/* register announcement port */
|
||||
strcpy(port->name, "Announce");
|
||||
port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
|
||||
port->kernel = NULL;
|
||||
port->type = 0;
|
||||
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
|
||||
port->addr.client = sysclient;
|
||||
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
|
||||
snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
|
||||
announce_port = port->addr.port;
|
||||
|
||||
kfree(inf);
|
||||
kfree(port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* unregister our internal client */
|
||||
void __exit snd_seq_system_client_done(void)
|
||||
{
|
||||
int oldsysclient = sysclient;
|
||||
|
||||
if (oldsysclient >= 0) {
|
||||
sysclient = -1;
|
||||
announce_port = -1;
|
||||
snd_seq_delete_kernel_client(oldsysclient);
|
||||
}
|
||||
}
|
||||
46
sound/core/seq/seq_system.h
Normal file
46
sound/core/seq/seq_system.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* ALSA sequencer System Client
|
||||
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_SYSTEM_H
|
||||
#define __SND_SEQ_SYSTEM_H
|
||||
|
||||
#include <sound/seq_kernel.h>
|
||||
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
void snd_seq_system_broadcast(int client, int port, int type);
|
||||
|
||||
#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
|
||||
#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
|
||||
#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
|
||||
#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
|
||||
#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
|
||||
#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
|
||||
|
||||
int snd_seq_system_notify(int client, int port, snd_seq_event_t *ev);
|
||||
|
||||
/* register our internal client */
|
||||
int snd_seq_system_client_init(void);
|
||||
|
||||
/* unregister our internal client */
|
||||
void snd_seq_system_client_done(void);
|
||||
|
||||
|
||||
#endif
|
||||
435
sound/core/seq/seq_timer.c
Normal file
435
sound/core/seq/seq_timer.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* ALSA sequencer Timer
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
* Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <linux/slab.h>
|
||||
#include "seq_timer.h"
|
||||
#include "seq_queue.h"
|
||||
#include "seq_info.h"
|
||||
|
||||
extern int seq_default_timer_class;
|
||||
extern int seq_default_timer_sclass;
|
||||
extern int seq_default_timer_card;
|
||||
extern int seq_default_timer_device;
|
||||
extern int seq_default_timer_subdevice;
|
||||
extern int seq_default_timer_resolution;
|
||||
|
||||
#define SKEW_BASE 0x10000 /* 16bit shift */
|
||||
|
||||
void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
|
||||
{
|
||||
if (tempo < 1000000)
|
||||
tick->resolution = (tempo * 1000) / ppq;
|
||||
else {
|
||||
/* might overflow.. */
|
||||
unsigned int s;
|
||||
s = tempo % ppq;
|
||||
s = (s * 1000) / ppq;
|
||||
tick->resolution = (tempo / ppq) * 1000;
|
||||
tick->resolution += s;
|
||||
}
|
||||
if (tick->resolution <= 0)
|
||||
tick->resolution = 1;
|
||||
tick->resolution *= nticks;
|
||||
snd_seq_timer_update_tick(tick, 0);
|
||||
}
|
||||
|
||||
/* create new timer (constructor) */
|
||||
seq_timer_t *snd_seq_timer_new(void)
|
||||
{
|
||||
seq_timer_t *tmr;
|
||||
|
||||
tmr = kcalloc(1, sizeof(*tmr), GFP_KERNEL);
|
||||
if (tmr == NULL) {
|
||||
snd_printd("malloc failed for snd_seq_timer_new() \n");
|
||||
return NULL;
|
||||
}
|
||||
spin_lock_init(&tmr->lock);
|
||||
|
||||
/* reset setup to defaults */
|
||||
snd_seq_timer_defaults(tmr);
|
||||
|
||||
/* reset time */
|
||||
snd_seq_timer_reset(tmr);
|
||||
|
||||
return tmr;
|
||||
}
|
||||
|
||||
/* delete timer (destructor) */
|
||||
void snd_seq_timer_delete(seq_timer_t **tmr)
|
||||
{
|
||||
seq_timer_t *t = *tmr;
|
||||
*tmr = NULL;
|
||||
|
||||
if (t == NULL) {
|
||||
snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
|
||||
return;
|
||||
}
|
||||
t->running = 0;
|
||||
|
||||
/* reset time */
|
||||
snd_seq_timer_stop(t);
|
||||
snd_seq_timer_reset(t);
|
||||
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
void snd_seq_timer_defaults(seq_timer_t * tmr)
|
||||
{
|
||||
/* setup defaults */
|
||||
tmr->ppq = 96; /* 96 PPQ */
|
||||
tmr->tempo = 500000; /* 120 BPM */
|
||||
snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
|
||||
tmr->running = 0;
|
||||
|
||||
tmr->type = SNDRV_SEQ_TIMER_ALSA;
|
||||
tmr->alsa_id.dev_class = seq_default_timer_class;
|
||||
tmr->alsa_id.dev_sclass = seq_default_timer_sclass;
|
||||
tmr->alsa_id.card = seq_default_timer_card;
|
||||
tmr->alsa_id.device = seq_default_timer_device;
|
||||
tmr->alsa_id.subdevice = seq_default_timer_subdevice;
|
||||
tmr->preferred_resolution = seq_default_timer_resolution;
|
||||
|
||||
tmr->skew = tmr->skew_base = SKEW_BASE;
|
||||
}
|
||||
|
||||
void snd_seq_timer_reset(seq_timer_t * tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
|
||||
/* reset time & songposition */
|
||||
tmr->cur_time.tv_sec = 0;
|
||||
tmr->cur_time.tv_nsec = 0;
|
||||
|
||||
tmr->tick.cur_tick = 0;
|
||||
tmr->tick.fraction = 0;
|
||||
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/* called by timer interrupt routine. the period time since previous invocation is passed */
|
||||
static void snd_seq_timer_interrupt(snd_timer_instance_t *timeri,
|
||||
unsigned long resolution,
|
||||
unsigned long ticks)
|
||||
{
|
||||
unsigned long flags;
|
||||
queue_t *q = (queue_t *)timeri->callback_data;
|
||||
seq_timer_t *tmr;
|
||||
|
||||
if (q == NULL)
|
||||
return;
|
||||
tmr = q->timer;
|
||||
if (tmr == NULL)
|
||||
return;
|
||||
if (!tmr->running)
|
||||
return;
|
||||
|
||||
resolution *= ticks;
|
||||
if (tmr->skew != tmr->skew_base) {
|
||||
/* FIXME: assuming skew_base = 0x10000 */
|
||||
resolution = (resolution >> 16) * tmr->skew +
|
||||
(((resolution & 0xffff) * tmr->skew) >> 16);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
|
||||
/* update timer */
|
||||
snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
|
||||
|
||||
/* calculate current tick */
|
||||
snd_seq_timer_update_tick(&tmr->tick, resolution);
|
||||
|
||||
/* register actual time of this timer update */
|
||||
do_gettimeofday(&tmr->last_update);
|
||||
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
|
||||
/* check queues and dispatch events */
|
||||
snd_seq_check_queue(q, 1, 0);
|
||||
}
|
||||
|
||||
/* set current tempo */
|
||||
int snd_seq_timer_set_tempo(seq_timer_t * tmr, int tempo)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(tmr, return -EINVAL);
|
||||
if (tempo <= 0)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
if ((unsigned int)tempo != tmr->tempo) {
|
||||
tmr->tempo = tempo;
|
||||
snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
|
||||
}
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set current ppq */
|
||||
int snd_seq_timer_set_ppq(seq_timer_t * tmr, int ppq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(tmr, return -EINVAL);
|
||||
if (ppq <= 0)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
if (tmr->running && (ppq != tmr->ppq)) {
|
||||
/* refuse to change ppq on running timers */
|
||||
/* because it will upset the song position (ticks) */
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
snd_printd("seq: cannot change ppq of a running timer\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
tmr->ppq = ppq;
|
||||
snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq, 1);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set current tick position */
|
||||
int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(tmr, return -EINVAL);
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
tmr->tick.cur_tick = position;
|
||||
tmr->tick.fraction = 0;
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set current real-time position */
|
||||
int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(tmr, return -EINVAL);
|
||||
|
||||
snd_seq_sanity_real_time(&position);
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
tmr->cur_time = position;
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set timer skew */
|
||||
int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
snd_assert(tmr, return -EINVAL);
|
||||
|
||||
/* FIXME */
|
||||
if (base != SKEW_BASE) {
|
||||
snd_printd("invalid skew base 0x%x\n", base);
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
tmr->skew = skew;
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_open(queue_t *q)
|
||||
{
|
||||
snd_timer_instance_t *t;
|
||||
seq_timer_t *tmr;
|
||||
char str[32];
|
||||
int err;
|
||||
|
||||
tmr = q->timer;
|
||||
snd_assert(tmr != NULL, return -EINVAL);
|
||||
if (tmr->timeri)
|
||||
return -EBUSY;
|
||||
sprintf(str, "sequencer queue %i", q->queue);
|
||||
if (tmr->type != SNDRV_SEQ_TIMER_ALSA) /* standard ALSA timer */
|
||||
return -EINVAL;
|
||||
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
|
||||
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
|
||||
err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
|
||||
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
|
||||
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
|
||||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
|
||||
snd_timer_id_t tid;
|
||||
memset(&tid, 0, sizeof(tid));
|
||||
tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
|
||||
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
|
||||
tid.card = -1;
|
||||
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
|
||||
err = snd_timer_open(&t, str, &tid, q->queue);
|
||||
}
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
t->callback = snd_seq_timer_interrupt;
|
||||
t->callback_data = q;
|
||||
t->flags |= SNDRV_TIMER_IFLG_AUTO;
|
||||
tmr->timeri = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_close(queue_t *q)
|
||||
{
|
||||
seq_timer_t *tmr;
|
||||
|
||||
tmr = q->timer;
|
||||
snd_assert(tmr != NULL, return -EINVAL);
|
||||
if (tmr->timeri) {
|
||||
snd_timer_stop(tmr->timeri);
|
||||
snd_timer_close(tmr->timeri);
|
||||
tmr->timeri = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_stop(seq_timer_t * tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
if (!tmr->running)
|
||||
return 0;
|
||||
tmr->running = 0;
|
||||
snd_timer_pause(tmr->timeri);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int initialize_timer(seq_timer_t *tmr)
|
||||
{
|
||||
snd_timer_t *t;
|
||||
t = tmr->timeri->timer;
|
||||
snd_assert(t, return -EINVAL);
|
||||
|
||||
tmr->ticks = 1;
|
||||
if (tmr->preferred_resolution &&
|
||||
! (t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
|
||||
unsigned long r = t->hw.resolution;
|
||||
if (! r && t->hw.c_resolution)
|
||||
r = t->hw.c_resolution(t);
|
||||
if (r) {
|
||||
tmr->ticks = (unsigned int)(1000000000uL / (r * tmr->preferred_resolution));
|
||||
if (! tmr->ticks)
|
||||
tmr->ticks = 1;
|
||||
}
|
||||
}
|
||||
tmr->initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_start(seq_timer_t * tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
if (tmr->running)
|
||||
snd_seq_timer_stop(tmr);
|
||||
snd_seq_timer_reset(tmr);
|
||||
if (initialize_timer(tmr) < 0)
|
||||
return -EINVAL;
|
||||
snd_timer_start(tmr->timeri, tmr->ticks);
|
||||
tmr->running = 1;
|
||||
do_gettimeofday(&tmr->last_update);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_continue(seq_timer_t * tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
if (tmr->running)
|
||||
return -EBUSY;
|
||||
if (! tmr->initialized) {
|
||||
snd_seq_timer_reset(tmr);
|
||||
if (initialize_timer(tmr) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_timer_start(tmr->timeri, tmr->ticks);
|
||||
tmr->running = 1;
|
||||
do_gettimeofday(&tmr->last_update);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return current 'real' time. use timeofday() to get better granularity. */
|
||||
snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr)
|
||||
{
|
||||
snd_seq_real_time_t cur_time;
|
||||
|
||||
cur_time = tmr->cur_time;
|
||||
if (tmr->running) {
|
||||
struct timeval tm;
|
||||
int usec;
|
||||
do_gettimeofday(&tm);
|
||||
usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
|
||||
if (usec < 0) {
|
||||
cur_time.tv_nsec += (1000000 + usec) * 1000;
|
||||
cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
|
||||
} else {
|
||||
cur_time.tv_nsec += usec * 1000;
|
||||
cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
|
||||
}
|
||||
snd_seq_sanity_real_time(&cur_time);
|
||||
}
|
||||
|
||||
return cur_time;
|
||||
}
|
||||
|
||||
/* TODO: use interpolation on tick queue (will only be useful for very
|
||||
high PPQ values) */
|
||||
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr)
|
||||
{
|
||||
return tmr->tick.cur_tick;
|
||||
}
|
||||
|
||||
|
||||
/* exported to seq_info.c */
|
||||
void snd_seq_info_timer_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
int idx;
|
||||
queue_t *q;
|
||||
seq_timer_t *tmr;
|
||||
snd_timer_instance_t *ti;
|
||||
unsigned long resolution;
|
||||
|
||||
for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
|
||||
q = queueptr(idx);
|
||||
if (q == NULL)
|
||||
continue;
|
||||
if ((tmr = q->timer) == NULL ||
|
||||
(ti = tmr->timeri) == NULL) {
|
||||
queuefree(q);
|
||||
continue;
|
||||
}
|
||||
snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
|
||||
resolution = snd_timer_resolution(ti) * tmr->ticks;
|
||||
snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
|
||||
snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
|
||||
queuefree(q);
|
||||
}
|
||||
}
|
||||
141
sound/core/seq/seq_timer.h
Normal file
141
sound/core/seq/seq_timer.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* ALSA sequencer Timer
|
||||
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#ifndef __SND_SEQ_TIMER_H
|
||||
#define __SND_SEQ_TIMER_H
|
||||
|
||||
#include <sound/timer.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
|
||||
typedef struct {
|
||||
snd_seq_tick_time_t cur_tick; /* current tick */
|
||||
unsigned long resolution; /* time per tick in nsec */
|
||||
unsigned long fraction; /* current time per tick in nsec */
|
||||
} seq_timer_tick_t;
|
||||
|
||||
typedef struct {
|
||||
/* ... tempo / offset / running state */
|
||||
|
||||
unsigned int running:1, /* running state of queue */
|
||||
initialized:1; /* timer is initialized */
|
||||
|
||||
unsigned int tempo; /* current tempo, us/tick */
|
||||
int ppq; /* time resolution, ticks/quarter */
|
||||
|
||||
snd_seq_real_time_t cur_time; /* current time */
|
||||
seq_timer_tick_t tick; /* current tick */
|
||||
int tick_updated;
|
||||
|
||||
int type; /* timer type */
|
||||
snd_timer_id_t alsa_id; /* ALSA's timer ID */
|
||||
snd_timer_instance_t *timeri; /* timer instance */
|
||||
unsigned int ticks;
|
||||
unsigned long preferred_resolution; /* timer resolution, ticks/sec */
|
||||
|
||||
unsigned int skew;
|
||||
unsigned int skew_base;
|
||||
|
||||
struct timeval last_update; /* time of last clock update, used for interpolation */
|
||||
|
||||
spinlock_t lock;
|
||||
} seq_timer_t;
|
||||
|
||||
|
||||
/* create new timer (constructor) */
|
||||
extern seq_timer_t *snd_seq_timer_new(void);
|
||||
|
||||
/* delete timer (destructor) */
|
||||
extern void snd_seq_timer_delete(seq_timer_t **tmr);
|
||||
|
||||
void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
|
||||
|
||||
/* */
|
||||
static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
|
||||
{
|
||||
if (tick->resolution > 0) {
|
||||
tick->fraction += resolution;
|
||||
tick->cur_tick += (unsigned int)(tick->fraction / tick->resolution);
|
||||
tick->fraction %= tick->resolution;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* compare timestamp between events */
|
||||
/* return 1 if a >= b; otherwise return 0 */
|
||||
static inline int snd_seq_compare_tick_time(snd_seq_tick_time_t *a, snd_seq_tick_time_t *b)
|
||||
{
|
||||
/* compare ticks */
|
||||
return (*a >= *b);
|
||||
}
|
||||
|
||||
static inline int snd_seq_compare_real_time(snd_seq_real_time_t *a, snd_seq_real_time_t *b)
|
||||
{
|
||||
/* compare real time */
|
||||
if (a->tv_sec > b->tv_sec)
|
||||
return 1;
|
||||
if ((a->tv_sec == b->tv_sec) && (a->tv_nsec >= b->tv_nsec))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void snd_seq_sanity_real_time(snd_seq_real_time_t *tm)
|
||||
{
|
||||
while (tm->tv_nsec >= 1000000000) {
|
||||
/* roll-over */
|
||||
tm->tv_nsec -= 1000000000;
|
||||
tm->tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* increment timestamp */
|
||||
static inline void snd_seq_inc_real_time(snd_seq_real_time_t *tm, snd_seq_real_time_t *inc)
|
||||
{
|
||||
tm->tv_sec += inc->tv_sec;
|
||||
tm->tv_nsec += inc->tv_nsec;
|
||||
snd_seq_sanity_real_time(tm);
|
||||
}
|
||||
|
||||
static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long nsec)
|
||||
{
|
||||
tm->tv_nsec += nsec;
|
||||
snd_seq_sanity_real_time(tm);
|
||||
}
|
||||
|
||||
/* called by timer isr */
|
||||
int snd_seq_timer_open(queue_t *q);
|
||||
int snd_seq_timer_close(queue_t *q);
|
||||
int snd_seq_timer_midi_open(queue_t *q);
|
||||
int snd_seq_timer_midi_close(queue_t *q);
|
||||
void snd_seq_timer_defaults(seq_timer_t *tmr);
|
||||
void snd_seq_timer_reset(seq_timer_t *tmr);
|
||||
int snd_seq_timer_stop(seq_timer_t *tmr);
|
||||
int snd_seq_timer_start(seq_timer_t *tmr);
|
||||
int snd_seq_timer_continue(seq_timer_t *tmr);
|
||||
int snd_seq_timer_set_tempo(seq_timer_t *tmr, int tempo);
|
||||
int snd_seq_timer_set_ppq(seq_timer_t *tmr, int ppq);
|
||||
int snd_seq_timer_set_position_tick(seq_timer_t *tmr, snd_seq_tick_time_t position);
|
||||
int snd_seq_timer_set_position_time(seq_timer_t *tmr, snd_seq_real_time_t position);
|
||||
int snd_seq_timer_set_skew(seq_timer_t *tmr, unsigned int skew, unsigned int base);
|
||||
snd_seq_real_time_t snd_seq_timer_get_cur_time(seq_timer_t *tmr);
|
||||
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(seq_timer_t *tmr);
|
||||
|
||||
#endif
|
||||
551
sound/core/seq/seq_virmidi.c
Normal file
551
sound/core/seq/seq_virmidi.c
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Virtual Raw MIDI client on Sequencer
|
||||
*
|
||||
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
|
||||
* Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Virtual Raw MIDI client
|
||||
*
|
||||
* The virtual rawmidi client is a sequencer client which associate
|
||||
* a rawmidi device file. The created rawmidi device file can be
|
||||
* accessed as a normal raw midi, but its MIDI source and destination
|
||||
* are arbitrary. For example, a user-client software synth connected
|
||||
* to this port can be used as a normal midi device as well.
|
||||
*
|
||||
* The virtual rawmidi device accepts also multiple opens. Each file
|
||||
* has its own input buffer, so that no conflict would occur. The drain
|
||||
* of input/output buffer acts only to the local buffer.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/rawmidi.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/seq_kernel.h>
|
||||
#include <sound/seq_midi_event.h>
|
||||
#include <sound/seq_virmidi.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* initialize an event record
|
||||
*/
|
||||
static void snd_virmidi_init_event(snd_virmidi_t *vmidi, snd_seq_event_t *ev)
|
||||
{
|
||||
memset(ev, 0, sizeof(*ev));
|
||||
ev->source.port = vmidi->port;
|
||||
switch (vmidi->seq_mode) {
|
||||
case SNDRV_VIRMIDI_SEQ_DISPATCH:
|
||||
ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
|
||||
break;
|
||||
case SNDRV_VIRMIDI_SEQ_ATTACH:
|
||||
/* FIXME: source and destination are same - not good.. */
|
||||
ev->dest.client = vmidi->client;
|
||||
ev->dest.port = vmidi->port;
|
||||
break;
|
||||
}
|
||||
ev->type = SNDRV_SEQ_EVENT_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* decode input event and put to read buffer of each opened file
|
||||
*/
|
||||
static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_t *ev)
|
||||
{
|
||||
snd_virmidi_t *vmidi;
|
||||
struct list_head *list;
|
||||
unsigned char msg[4];
|
||||
int len;
|
||||
|
||||
read_lock(&rdev->filelist_lock);
|
||||
list_for_each(list, &rdev->filelist) {
|
||||
vmidi = list_entry(list, snd_virmidi_t, list);
|
||||
if (!vmidi->trigger)
|
||||
continue;
|
||||
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
|
||||
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
|
||||
continue;
|
||||
snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
|
||||
} else {
|
||||
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
|
||||
if (len > 0)
|
||||
snd_rawmidi_receive(vmidi->substream, msg, len);
|
||||
}
|
||||
}
|
||||
read_unlock(&rdev->filelist_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* receive an event from the remote virmidi port
|
||||
*
|
||||
* for rawmidi inputs, you can call this function from the event
|
||||
* handler of a remote port which is attached to the virmidi via
|
||||
* SNDRV_VIRMIDI_SEQ_ATTACH.
|
||||
*/
|
||||
/* exported */
|
||||
int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = rmidi->private_data;
|
||||
return snd_virmidi_dev_receive_event(rdev, ev);
|
||||
}
|
||||
|
||||
/*
|
||||
* event handler of virmidi port
|
||||
*/
|
||||
static int snd_virmidi_event_input(snd_seq_event_t *ev, int direct,
|
||||
void *private_data, int atomic, int hop)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = private_data;
|
||||
if (!(rdev->flags & SNDRV_VIRMIDI_USE))
|
||||
return 0; /* ignored */
|
||||
return snd_virmidi_dev_receive_event(rdev, ev);
|
||||
}
|
||||
|
||||
/*
|
||||
* trigger rawmidi stream for input
|
||||
*/
|
||||
static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up)
|
||||
{
|
||||
snd_virmidi_t *vmidi = substream->runtime->private_data;
|
||||
|
||||
if (up) {
|
||||
vmidi->trigger = 1;
|
||||
} else {
|
||||
vmidi->trigger = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* trigger rawmidi stream for output
|
||||
*/
|
||||
static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up)
|
||||
{
|
||||
snd_virmidi_t *vmidi = substream->runtime->private_data;
|
||||
int count, res;
|
||||
unsigned char buf[32], *pbuf;
|
||||
|
||||
if (up) {
|
||||
vmidi->trigger = 1;
|
||||
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
|
||||
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
|
||||
snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
|
||||
return; /* ignored */
|
||||
}
|
||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
|
||||
return;
|
||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||
}
|
||||
while (1) {
|
||||
count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
|
||||
if (count <= 0)
|
||||
break;
|
||||
pbuf = buf;
|
||||
while (count > 0) {
|
||||
res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
|
||||
if (res < 0) {
|
||||
snd_midi_event_reset_encode(vmidi->parser);
|
||||
continue;
|
||||
}
|
||||
snd_rawmidi_transmit_ack(substream, res);
|
||||
pbuf += res;
|
||||
count -= res;
|
||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
|
||||
return;
|
||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vmidi->trigger = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* open rawmidi handle for input
|
||||
*/
|
||||
static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
|
||||
snd_rawmidi_runtime_t *runtime = substream->runtime;
|
||||
snd_virmidi_t *vmidi;
|
||||
unsigned long flags;
|
||||
|
||||
vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
|
||||
if (vmidi == NULL)
|
||||
return -ENOMEM;
|
||||
vmidi->substream = substream;
|
||||
if (snd_midi_event_new(0, &vmidi->parser) < 0) {
|
||||
kfree(vmidi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
vmidi->seq_mode = rdev->seq_mode;
|
||||
vmidi->client = rdev->client;
|
||||
vmidi->port = rdev->port;
|
||||
runtime->private_data = vmidi;
|
||||
write_lock_irqsave(&rdev->filelist_lock, flags);
|
||||
list_add_tail(&vmidi->list, &rdev->filelist);
|
||||
write_unlock_irqrestore(&rdev->filelist_lock, flags);
|
||||
vmidi->rdev = rdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* open rawmidi handle for output
|
||||
*/
|
||||
static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev = substream->rmidi->private_data;
|
||||
snd_rawmidi_runtime_t *runtime = substream->runtime;
|
||||
snd_virmidi_t *vmidi;
|
||||
|
||||
vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL);
|
||||
if (vmidi == NULL)
|
||||
return -ENOMEM;
|
||||
vmidi->substream = substream;
|
||||
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
|
||||
kfree(vmidi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
vmidi->seq_mode = rdev->seq_mode;
|
||||
vmidi->client = rdev->client;
|
||||
vmidi->port = rdev->port;
|
||||
snd_virmidi_init_event(vmidi, &vmidi->event);
|
||||
vmidi->rdev = rdev;
|
||||
runtime->private_data = vmidi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close rawmidi handle for input
|
||||
*/
|
||||
static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream)
|
||||
{
|
||||
snd_virmidi_t *vmidi = substream->runtime->private_data;
|
||||
snd_midi_event_free(vmidi->parser);
|
||||
list_del(&vmidi->list);
|
||||
substream->runtime->private_data = NULL;
|
||||
kfree(vmidi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close rawmidi handle for output
|
||||
*/
|
||||
static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream)
|
||||
{
|
||||
snd_virmidi_t *vmidi = substream->runtime->private_data;
|
||||
snd_midi_event_free(vmidi->parser);
|
||||
substream->runtime->private_data = NULL;
|
||||
kfree(vmidi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* subscribe callback - allow output to rawmidi device
|
||||
*/
|
||||
static int snd_virmidi_subscribe(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = private_data;
|
||||
if (!try_module_get(rdev->card->module))
|
||||
return -EFAULT;
|
||||
rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* unsubscribe callback - disallow output to rawmidi device
|
||||
*/
|
||||
static int snd_virmidi_unsubscribe(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = private_data;
|
||||
rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
|
||||
module_put(rdev->card->module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* use callback - allow input to rawmidi device
|
||||
*/
|
||||
static int snd_virmidi_use(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = private_data;
|
||||
if (!try_module_get(rdev->card->module))
|
||||
return -EFAULT;
|
||||
rdev->flags |= SNDRV_VIRMIDI_USE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* unuse callback - disallow input to rawmidi device
|
||||
*/
|
||||
static int snd_virmidi_unuse(void *private_data, snd_seq_port_subscribe_t *info)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev;
|
||||
|
||||
rdev = private_data;
|
||||
rdev->flags &= ~SNDRV_VIRMIDI_USE;
|
||||
module_put(rdev->card->module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Register functions
|
||||
*/
|
||||
|
||||
static snd_rawmidi_ops_t snd_virmidi_input_ops = {
|
||||
.open = snd_virmidi_input_open,
|
||||
.close = snd_virmidi_input_close,
|
||||
.trigger = snd_virmidi_input_trigger,
|
||||
};
|
||||
|
||||
static snd_rawmidi_ops_t snd_virmidi_output_ops = {
|
||||
.open = snd_virmidi_output_open,
|
||||
.close = snd_virmidi_output_close,
|
||||
.trigger = snd_virmidi_output_trigger,
|
||||
};
|
||||
|
||||
/*
|
||||
* create a sequencer client and a port
|
||||
*/
|
||||
static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
|
||||
{
|
||||
int client;
|
||||
snd_seq_client_callback_t callbacks;
|
||||
snd_seq_port_callback_t pcallbacks;
|
||||
snd_seq_client_info_t *info;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
int err;
|
||||
|
||||
if (rdev->client >= 0)
|
||||
return 0;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL);
|
||||
if (! info || ! pinfo) {
|
||||
err = -ENOMEM;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.private_data = rdev;
|
||||
callbacks.allow_input = 1;
|
||||
callbacks.allow_output = 1;
|
||||
client = snd_seq_create_kernel_client(rdev->card, rdev->device, &callbacks);
|
||||
if (client < 0) {
|
||||
err = client;
|
||||
goto __error;
|
||||
}
|
||||
rdev->client = client;
|
||||
|
||||
/* set client name */
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->client = client;
|
||||
info->type = KERNEL_CLIENT;
|
||||
sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
|
||||
snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
|
||||
|
||||
/* create a port */
|
||||
memset(pinfo, 0, sizeof(*pinfo));
|
||||
pinfo->addr.client = client;
|
||||
sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
|
||||
/* set all capabilities */
|
||||
pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
|
||||
pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
|
||||
pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
|
||||
pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
|
||||
pinfo->midi_channels = 16;
|
||||
memset(&pcallbacks, 0, sizeof(pcallbacks));
|
||||
pcallbacks.owner = THIS_MODULE;
|
||||
pcallbacks.private_data = rdev;
|
||||
pcallbacks.subscribe = snd_virmidi_subscribe;
|
||||
pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
|
||||
pcallbacks.use = snd_virmidi_use;
|
||||
pcallbacks.unuse = snd_virmidi_unuse;
|
||||
pcallbacks.event_input = snd_virmidi_event_input;
|
||||
pinfo->kernel = &pcallbacks;
|
||||
err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
|
||||
if (err < 0) {
|
||||
snd_seq_delete_kernel_client(client);
|
||||
rdev->client = -1;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
rdev->port = pinfo->addr.port;
|
||||
err = 0; /* success */
|
||||
|
||||
__error:
|
||||
kfree(info);
|
||||
kfree(pinfo);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release the sequencer client
|
||||
*/
|
||||
static void snd_virmidi_dev_detach_seq(snd_virmidi_dev_t *rdev)
|
||||
{
|
||||
if (rdev->client >= 0) {
|
||||
snd_seq_delete_kernel_client(rdev->client);
|
||||
rdev->client = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* register the device
|
||||
*/
|
||||
static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev = rmidi->private_data;
|
||||
int err;
|
||||
|
||||
switch (rdev->seq_mode) {
|
||||
case SNDRV_VIRMIDI_SEQ_DISPATCH:
|
||||
err = snd_virmidi_dev_attach_seq(rdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case SNDRV_VIRMIDI_SEQ_ATTACH:
|
||||
if (rdev->client == 0)
|
||||
return -EINVAL;
|
||||
/* should check presence of port more strictly.. */
|
||||
break;
|
||||
default:
|
||||
snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* unregister the device
|
||||
*/
|
||||
static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev = rmidi->private_data;
|
||||
|
||||
if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
|
||||
snd_virmidi_dev_detach_seq(rdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static snd_rawmidi_global_ops_t snd_virmidi_global_ops = {
|
||||
.dev_register = snd_virmidi_dev_register,
|
||||
.dev_unregister = snd_virmidi_dev_unregister,
|
||||
};
|
||||
|
||||
/*
|
||||
* free device
|
||||
*/
|
||||
static void snd_virmidi_free(snd_rawmidi_t *rmidi)
|
||||
{
|
||||
snd_virmidi_dev_t *rdev = rmidi->private_data;
|
||||
kfree(rdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new device
|
||||
*
|
||||
*/
|
||||
/* exported */
|
||||
int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi)
|
||||
{
|
||||
snd_rawmidi_t *rmidi;
|
||||
snd_virmidi_dev_t *rdev;
|
||||
int err;
|
||||
|
||||
*rrmidi = NULL;
|
||||
if ((err = snd_rawmidi_new(card, "VirMidi", device,
|
||||
16, /* may be configurable */
|
||||
16, /* may be configurable */
|
||||
&rmidi)) < 0)
|
||||
return err;
|
||||
strcpy(rmidi->name, rmidi->id);
|
||||
rdev = kcalloc(1, sizeof(*rdev), GFP_KERNEL);
|
||||
if (rdev == NULL) {
|
||||
snd_device_free(card, rmidi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rdev->card = card;
|
||||
rdev->rmidi = rmidi;
|
||||
rdev->device = device;
|
||||
rdev->client = -1;
|
||||
rwlock_init(&rdev->filelist_lock);
|
||||
INIT_LIST_HEAD(&rdev->filelist);
|
||||
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
|
||||
rmidi->private_data = rdev;
|
||||
rmidi->private_free = snd_virmidi_free;
|
||||
rmidi->ops = &snd_virmidi_global_ops;
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
|
||||
rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
|
||||
SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
*rrmidi = rmidi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ENTRY functions
|
||||
*/
|
||||
|
||||
static int __init alsa_virmidi_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_virmidi_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_virmidi_init)
|
||||
module_exit(alsa_virmidi_exit)
|
||||
|
||||
EXPORT_SYMBOL(snd_virmidi_new);
|
||||
EXPORT_SYMBOL(snd_virmidi_receive);
|
||||
111
sound/core/sgbuf.c
Normal file
111
sound/core/sgbuf.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Scatter-Gather buffer
|
||||
*
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <sound/memalloc.h>
|
||||
|
||||
|
||||
/* table entries are align to 32 */
|
||||
#define SGBUF_TBL_ALIGN 32
|
||||
#define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
|
||||
|
||||
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf = dmab->private_data;
|
||||
struct snd_dma_buffer tmpb;
|
||||
int i;
|
||||
|
||||
if (! sgbuf)
|
||||
return -EINVAL;
|
||||
|
||||
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
tmpb.dev.dev = sgbuf->dev;
|
||||
for (i = 0; i < sgbuf->pages; i++) {
|
||||
tmpb.area = sgbuf->table[i].buf;
|
||||
tmpb.addr = sgbuf->table[i].addr;
|
||||
tmpb.bytes = PAGE_SIZE;
|
||||
snd_dma_free_pages(&tmpb);
|
||||
}
|
||||
if (dmab->area)
|
||||
vunmap(dmab->area);
|
||||
dmab->area = NULL;
|
||||
|
||||
kfree(sgbuf->table);
|
||||
kfree(sgbuf->page_table);
|
||||
kfree(sgbuf);
|
||||
dmab->private_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
size_t size, struct snd_dma_buffer *dmab,
|
||||
size_t *res_size)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf;
|
||||
unsigned int i, pages;
|
||||
struct snd_dma_buffer tmpb;
|
||||
|
||||
dmab->area = NULL;
|
||||
dmab->addr = 0;
|
||||
dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL);
|
||||
if (! sgbuf)
|
||||
return NULL;
|
||||
memset(sgbuf, 0, sizeof(*sgbuf));
|
||||
sgbuf->dev = device;
|
||||
pages = snd_sgbuf_aligned_pages(size);
|
||||
sgbuf->tblsize = sgbuf_align_table(pages);
|
||||
sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
|
||||
if (! sgbuf->table)
|
||||
goto _failed;
|
||||
memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize);
|
||||
sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
|
||||
if (! sgbuf->page_table)
|
||||
goto _failed;
|
||||
memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize);
|
||||
|
||||
/* allocate each page */
|
||||
for (i = 0; i < pages; i++) {
|
||||
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) {
|
||||
if (res_size == NULL)
|
||||
goto _failed;
|
||||
*res_size = size = sgbuf->pages * PAGE_SIZE;
|
||||
break;
|
||||
}
|
||||
sgbuf->table[i].buf = tmpb.area;
|
||||
sgbuf->table[i].addr = tmpb.addr;
|
||||
sgbuf->page_table[i] = virt_to_page(tmpb.area);
|
||||
sgbuf->pages++;
|
||||
}
|
||||
|
||||
sgbuf->size = size;
|
||||
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
|
||||
if (! dmab->area)
|
||||
goto _failed;
|
||||
return dmab->area;
|
||||
|
||||
_failed:
|
||||
snd_free_sgbuf_pages(dmab); /* free the table */
|
||||
return NULL;
|
||||
}
|
||||
491
sound/core/sound.c
Normal file
491
sound/core/sound.c
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Advanced Linux Sound Architecture
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/version.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/initval.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#define SNDRV_OS_MINORS 256
|
||||
|
||||
static int major = CONFIG_SND_MAJOR;
|
||||
int snd_major;
|
||||
static int cards_limit = 1;
|
||||
static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO;
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
|
||||
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards.");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_param(major, int, 0444);
|
||||
MODULE_PARM_DESC(major, "Major # for sound driver.");
|
||||
module_param(cards_limit, int, 0444);
|
||||
MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards.");
|
||||
#ifdef CONFIG_DEVFS_FS
|
||||
module_param(device_mode, int, 0444);
|
||||
MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs.");
|
||||
#endif
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
|
||||
|
||||
/* this one holds the actual max. card number currently available.
|
||||
* as default, it's identical with cards_limit option. when more
|
||||
* modules are loaded manually, this limit number increases, too.
|
||||
*/
|
||||
int snd_ecards_limit;
|
||||
|
||||
static struct list_head snd_minors_hash[SNDRV_CARDS];
|
||||
|
||||
static DECLARE_MUTEX(sound_mutex);
|
||||
|
||||
extern struct class_simple *sound_class;
|
||||
|
||||
|
||||
#ifdef CONFIG_KMOD
|
||||
|
||||
/**
|
||||
* snd_request_card - try to load the card module
|
||||
* @card: the card number
|
||||
*
|
||||
* Tries to load the module "snd-card-X" for the given card number
|
||||
* via KMOD. Returns immediately if already loaded.
|
||||
*/
|
||||
void snd_request_card(int card)
|
||||
{
|
||||
int locked;
|
||||
|
||||
if (! current->fs->root)
|
||||
return;
|
||||
read_lock(&snd_card_rwlock);
|
||||
locked = snd_cards_lock & (1 << card);
|
||||
read_unlock(&snd_card_rwlock);
|
||||
if (locked)
|
||||
return;
|
||||
if (card < 0 || card >= cards_limit)
|
||||
return;
|
||||
request_module("snd-card-%i", card);
|
||||
}
|
||||
|
||||
static void snd_request_other(int minor)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if (! current->fs->root)
|
||||
return;
|
||||
switch (minor) {
|
||||
case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break;
|
||||
case SNDRV_MINOR_TIMER: str = "snd-timer"; break;
|
||||
default: return;
|
||||
}
|
||||
request_module(str);
|
||||
}
|
||||
|
||||
#endif /* request_module support */
|
||||
|
||||
static snd_minor_t *snd_minor_search(int minor)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_minor_t *mptr;
|
||||
|
||||
list_for_each(list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]) {
|
||||
mptr = list_entry(list, snd_minor_t, list);
|
||||
if (mptr->number == minor)
|
||||
return mptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int snd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int minor = iminor(inode);
|
||||
int card = SNDRV_MINOR_CARD(minor);
|
||||
int dev = SNDRV_MINOR_DEVICE(minor);
|
||||
snd_minor_t *mptr = NULL;
|
||||
struct file_operations *old_fops;
|
||||
int err = 0;
|
||||
|
||||
if (dev != SNDRV_MINOR_SEQUENCER && dev != SNDRV_MINOR_TIMER) {
|
||||
if (snd_cards[card] == NULL) {
|
||||
#ifdef CONFIG_KMOD
|
||||
snd_request_card(card);
|
||||
if (snd_cards[card] == NULL)
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
} else {
|
||||
#ifdef CONFIG_KMOD
|
||||
if ((mptr = snd_minor_search(minor)) == NULL)
|
||||
snd_request_other(minor);
|
||||
#endif
|
||||
}
|
||||
if (mptr == NULL && (mptr = snd_minor_search(minor)) == NULL)
|
||||
return -ENODEV;
|
||||
old_fops = file->f_op;
|
||||
file->f_op = fops_get(mptr->f_ops);
|
||||
if (file->f_op->open)
|
||||
err = file->f_op->open(inode, file);
|
||||
if (err) {
|
||||
fops_put(file->f_op);
|
||||
file->f_op = fops_get(old_fops);
|
||||
}
|
||||
fops_put(old_fops);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct file_operations snd_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.open = snd_open
|
||||
};
|
||||
|
||||
static int snd_kernel_minor(int type, snd_card_t * card, int dev)
|
||||
{
|
||||
int minor;
|
||||
|
||||
switch (type) {
|
||||
case SNDRV_DEVICE_TYPE_SEQUENCER:
|
||||
case SNDRV_DEVICE_TYPE_TIMER:
|
||||
minor = type;
|
||||
break;
|
||||
case SNDRV_DEVICE_TYPE_CONTROL:
|
||||
snd_assert(card != NULL, return -EINVAL);
|
||||
minor = SNDRV_MINOR(card->number, type);
|
||||
break;
|
||||
case SNDRV_DEVICE_TYPE_HWDEP:
|
||||
case SNDRV_DEVICE_TYPE_RAWMIDI:
|
||||
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
|
||||
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
|
||||
snd_assert(card != NULL, return -EINVAL);
|
||||
minor = SNDRV_MINOR(card->number, type + dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
|
||||
return minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_register_device - Register the ALSA device file for the card
|
||||
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
|
||||
* @card: the card instance
|
||||
* @dev: the device index
|
||||
* @reg: the snd_minor_t record
|
||||
* @name: the device file name
|
||||
*
|
||||
* Registers an ALSA device file for the given card.
|
||||
* The operators have to be set in reg parameter.
|
||||
*
|
||||
* Retrurns zero if successful, or a negative error code on failure.
|
||||
*/
|
||||
int snd_register_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name)
|
||||
{
|
||||
int minor = snd_kernel_minor(type, card, dev);
|
||||
snd_minor_t *preg;
|
||||
struct device *device = NULL;
|
||||
|
||||
if (minor < 0)
|
||||
return minor;
|
||||
snd_assert(name, return -EINVAL);
|
||||
preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t) + strlen(name) + 1, GFP_KERNEL);
|
||||
if (preg == NULL)
|
||||
return -ENOMEM;
|
||||
*preg = *reg;
|
||||
preg->number = minor;
|
||||
preg->device = dev;
|
||||
strcpy(preg->name, name);
|
||||
down(&sound_mutex);
|
||||
if (snd_minor_search(minor)) {
|
||||
up(&sound_mutex);
|
||||
kfree(preg);
|
||||
return -EBUSY;
|
||||
}
|
||||
list_add_tail(&preg->list, &snd_minors_hash[SNDRV_MINOR_CARD(minor)]);
|
||||
if (strncmp(name, "controlC", 8) || card->number >= cards_limit)
|
||||
devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name);
|
||||
if (card)
|
||||
device = card->dev;
|
||||
class_simple_device_add(sound_class, MKDEV(major, minor), device, name);
|
||||
|
||||
up(&sound_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_unregister_device - unregister the device on the given card
|
||||
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
|
||||
* @card: the card instance
|
||||
* @dev: the device index
|
||||
*
|
||||
* Unregisters the device file already registered via
|
||||
* snd_register_device().
|
||||
*
|
||||
* Returns zero if sucecessful, or a negative error code on failure
|
||||
*/
|
||||
int snd_unregister_device(int type, snd_card_t * card, int dev)
|
||||
{
|
||||
int minor = snd_kernel_minor(type, card, dev);
|
||||
snd_minor_t *mptr;
|
||||
|
||||
if (minor < 0)
|
||||
return minor;
|
||||
down(&sound_mutex);
|
||||
if ((mptr = snd_minor_search(minor)) == NULL) {
|
||||
up(&sound_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(mptr->name, "controlC", 8) || card->number >= cards_limit) /* created in sound.c */
|
||||
devfs_remove("snd/%s", mptr->name);
|
||||
class_simple_device_remove(MKDEV(major, minor));
|
||||
|
||||
list_del(&mptr->list);
|
||||
up(&sound_mutex);
|
||||
kfree(mptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* INFO PART
|
||||
*/
|
||||
|
||||
static snd_info_entry_t *snd_minor_info_entry = NULL;
|
||||
|
||||
static void snd_minor_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
int card, device;
|
||||
struct list_head *list;
|
||||
snd_minor_t *mptr;
|
||||
|
||||
down(&sound_mutex);
|
||||
for (card = 0; card < SNDRV_CARDS; card++) {
|
||||
list_for_each(list, &snd_minors_hash[card]) {
|
||||
mptr = list_entry(list, snd_minor_t, list);
|
||||
if (SNDRV_MINOR_DEVICE(mptr->number) != SNDRV_MINOR_SEQUENCER) {
|
||||
if ((device = mptr->device) >= 0)
|
||||
snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, device, mptr->comment);
|
||||
else
|
||||
snd_iprintf(buffer, "%3i: [%i] : %s\n", mptr->number, card, mptr->comment);
|
||||
} else {
|
||||
snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
up(&sound_mutex);
|
||||
}
|
||||
|
||||
int __init snd_minor_info_init(void)
|
||||
{
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
|
||||
if (entry) {
|
||||
entry->c.text.read_size = PAGE_SIZE;
|
||||
entry->c.text.read = snd_minor_info_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
snd_minor_info_entry = entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_minor_info_done(void)
|
||||
{
|
||||
if (snd_minor_info_entry)
|
||||
snd_info_unregister(snd_minor_info_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT PART
|
||||
*/
|
||||
|
||||
static int __init alsa_sound_init(void)
|
||||
{
|
||||
short controlnum;
|
||||
int err;
|
||||
int card;
|
||||
|
||||
snd_major = major;
|
||||
snd_ecards_limit = cards_limit;
|
||||
for (card = 0; card < SNDRV_CARDS; card++)
|
||||
INIT_LIST_HEAD(&snd_minors_hash[card]);
|
||||
if ((err = snd_oss_init_module()) < 0)
|
||||
return err;
|
||||
devfs_mk_dir("snd");
|
||||
if (register_chrdev(major, "alsa", &snd_fops)) {
|
||||
snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
|
||||
devfs_remove("snd");
|
||||
return -EIO;
|
||||
}
|
||||
snd_memory_init();
|
||||
if (snd_info_init() < 0) {
|
||||
snd_memory_done();
|
||||
unregister_chrdev(major, "alsa");
|
||||
devfs_remove("snd");
|
||||
return -ENOMEM;
|
||||
}
|
||||
snd_info_minor_register();
|
||||
for (controlnum = 0; controlnum < cards_limit; controlnum++)
|
||||
devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum);
|
||||
#ifndef MODULE
|
||||
printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_sound_exit(void)
|
||||
{
|
||||
short controlnum;
|
||||
|
||||
for (controlnum = 0; controlnum < cards_limit; controlnum++)
|
||||
devfs_remove("snd/controlC%d", controlnum);
|
||||
|
||||
snd_info_minor_unregister();
|
||||
snd_info_done();
|
||||
snd_memory_done();
|
||||
if (unregister_chrdev(major, "alsa") != 0)
|
||||
snd_printk(KERN_ERR "unable to unregister major device number %d\n", major);
|
||||
devfs_remove("snd");
|
||||
}
|
||||
|
||||
module_init(alsa_sound_init)
|
||||
module_exit(alsa_sound_exit)
|
||||
|
||||
/* sound.c */
|
||||
EXPORT_SYMBOL(snd_major);
|
||||
EXPORT_SYMBOL(snd_ecards_limit);
|
||||
#if defined(CONFIG_KMOD)
|
||||
EXPORT_SYMBOL(snd_request_card);
|
||||
#endif
|
||||
EXPORT_SYMBOL(snd_register_device);
|
||||
EXPORT_SYMBOL(snd_unregister_device);
|
||||
#if defined(CONFIG_SND_OSSEMUL)
|
||||
EXPORT_SYMBOL(snd_register_oss_device);
|
||||
EXPORT_SYMBOL(snd_unregister_oss_device);
|
||||
#endif
|
||||
/* memory.c */
|
||||
#ifdef CONFIG_SND_DEBUG_MEMORY
|
||||
EXPORT_SYMBOL(snd_hidden_kmalloc);
|
||||
EXPORT_SYMBOL(snd_hidden_kcalloc);
|
||||
EXPORT_SYMBOL(snd_hidden_kfree);
|
||||
EXPORT_SYMBOL(snd_hidden_vmalloc);
|
||||
EXPORT_SYMBOL(snd_hidden_vfree);
|
||||
#endif
|
||||
EXPORT_SYMBOL(snd_kmalloc_strdup);
|
||||
EXPORT_SYMBOL(copy_to_user_fromio);
|
||||
EXPORT_SYMBOL(copy_from_user_toio);
|
||||
/* init.c */
|
||||
EXPORT_SYMBOL(snd_cards);
|
||||
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
|
||||
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
|
||||
#endif
|
||||
EXPORT_SYMBOL(snd_card_new);
|
||||
EXPORT_SYMBOL(snd_card_disconnect);
|
||||
EXPORT_SYMBOL(snd_card_free);
|
||||
EXPORT_SYMBOL(snd_card_free_in_thread);
|
||||
EXPORT_SYMBOL(snd_card_register);
|
||||
EXPORT_SYMBOL(snd_component_add);
|
||||
EXPORT_SYMBOL(snd_card_file_add);
|
||||
EXPORT_SYMBOL(snd_card_file_remove);
|
||||
#ifdef CONFIG_PM
|
||||
EXPORT_SYMBOL(snd_power_wait);
|
||||
EXPORT_SYMBOL(snd_card_set_pm_callback);
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_SND_GENERIC_PM)
|
||||
EXPORT_SYMBOL(snd_card_set_generic_pm_callback);
|
||||
#endif
|
||||
#ifdef CONFIG_PCI
|
||||
EXPORT_SYMBOL(snd_card_pci_suspend);
|
||||
EXPORT_SYMBOL(snd_card_pci_resume);
|
||||
#endif
|
||||
#endif
|
||||
/* device.c */
|
||||
EXPORT_SYMBOL(snd_device_new);
|
||||
EXPORT_SYMBOL(snd_device_register);
|
||||
EXPORT_SYMBOL(snd_device_free);
|
||||
EXPORT_SYMBOL(snd_device_free_all);
|
||||
/* isadma.c */
|
||||
#ifdef CONFIG_ISA
|
||||
EXPORT_SYMBOL(snd_dma_program);
|
||||
EXPORT_SYMBOL(snd_dma_disable);
|
||||
EXPORT_SYMBOL(snd_dma_pointer);
|
||||
#endif
|
||||
/* info.c */
|
||||
#ifdef CONFIG_PROC_FS
|
||||
EXPORT_SYMBOL(snd_seq_root);
|
||||
EXPORT_SYMBOL(snd_iprintf);
|
||||
EXPORT_SYMBOL(snd_info_get_line);
|
||||
EXPORT_SYMBOL(snd_info_get_str);
|
||||
EXPORT_SYMBOL(snd_info_create_module_entry);
|
||||
EXPORT_SYMBOL(snd_info_create_card_entry);
|
||||
EXPORT_SYMBOL(snd_info_free_entry);
|
||||
EXPORT_SYMBOL(snd_info_register);
|
||||
EXPORT_SYMBOL(snd_info_unregister);
|
||||
EXPORT_SYMBOL(snd_card_proc_new);
|
||||
#endif
|
||||
/* info_oss.c */
|
||||
#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
|
||||
EXPORT_SYMBOL(snd_oss_info_register);
|
||||
#endif
|
||||
/* control.c */
|
||||
EXPORT_SYMBOL(snd_ctl_new);
|
||||
EXPORT_SYMBOL(snd_ctl_new1);
|
||||
EXPORT_SYMBOL(snd_ctl_free_one);
|
||||
EXPORT_SYMBOL(snd_ctl_add);
|
||||
EXPORT_SYMBOL(snd_ctl_remove);
|
||||
EXPORT_SYMBOL(snd_ctl_remove_id);
|
||||
EXPORT_SYMBOL(snd_ctl_rename_id);
|
||||
EXPORT_SYMBOL(snd_ctl_find_numid);
|
||||
EXPORT_SYMBOL(snd_ctl_find_id);
|
||||
EXPORT_SYMBOL(snd_ctl_notify);
|
||||
EXPORT_SYMBOL(snd_ctl_register_ioctl);
|
||||
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
|
||||
#ifdef CONFIG_COMPAT
|
||||
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
|
||||
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
|
||||
#endif
|
||||
EXPORT_SYMBOL(snd_ctl_elem_read);
|
||||
EXPORT_SYMBOL(snd_ctl_elem_write);
|
||||
/* misc.c */
|
||||
EXPORT_SYMBOL(snd_task_name);
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
EXPORT_SYMBOL(snd_verbose_printk);
|
||||
#endif
|
||||
#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
|
||||
EXPORT_SYMBOL(snd_verbose_printd);
|
||||
#endif
|
||||
/* wrappers */
|
||||
#ifdef CONFIG_SND_DEBUG_MEMORY
|
||||
EXPORT_SYMBOL(snd_wrapper_kmalloc);
|
||||
EXPORT_SYMBOL(snd_wrapper_kfree);
|
||||
EXPORT_SYMBOL(snd_wrapper_vmalloc);
|
||||
EXPORT_SYMBOL(snd_wrapper_vfree);
|
||||
#endif
|
||||
250
sound/core/sound_oss.c
Normal file
250
sound/core/sound_oss.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Advanced Linux Sound Architecture
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sound/driver.h>
|
||||
|
||||
#ifdef CONFIG_SND_OSSEMUL
|
||||
|
||||
#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
|
||||
#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
|
||||
#endif
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
#include <sound/info.h>
|
||||
#include <linux/sound.h>
|
||||
|
||||
#define SNDRV_OS_MINORS 256
|
||||
|
||||
static struct list_head snd_oss_minors_hash[SNDRV_CARDS];
|
||||
|
||||
static DECLARE_MUTEX(sound_oss_mutex);
|
||||
|
||||
static snd_minor_t *snd_oss_minor_search(int minor)
|
||||
{
|
||||
struct list_head *list;
|
||||
snd_minor_t *mptr;
|
||||
|
||||
list_for_each(list, &snd_oss_minors_hash[SNDRV_MINOR_OSS_CARD(minor)]) {
|
||||
mptr = list_entry(list, snd_minor_t, list);
|
||||
if (mptr->number == minor)
|
||||
return mptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int snd_oss_kernel_minor(int type, snd_card_t * card, int dev)
|
||||
{
|
||||
int minor;
|
||||
|
||||
switch (type) {
|
||||
case SNDRV_OSS_DEVICE_TYPE_MIXER:
|
||||
snd_assert(card != NULL && dev <= 1, return -EINVAL);
|
||||
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_SEQUENCER:
|
||||
minor = SNDRV_MINOR_OSS_SEQUENCER;
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_MUSIC:
|
||||
minor = SNDRV_MINOR_OSS_MUSIC;
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_PCM:
|
||||
snd_assert(card != NULL && dev <= 1, return -EINVAL);
|
||||
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_MIDI:
|
||||
snd_assert(card != NULL && dev <= 1, return -EINVAL);
|
||||
minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_DMFM:
|
||||
minor = SNDRV_MINOR_OSS(card->number, SNDRV_MINOR_OSS_DMFM);
|
||||
break;
|
||||
case SNDRV_OSS_DEVICE_TYPE_SNDSTAT:
|
||||
minor = SNDRV_MINOR_OSS_SNDSTAT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL);
|
||||
return minor;
|
||||
}
|
||||
|
||||
int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * reg, const char *name)
|
||||
{
|
||||
int minor = snd_oss_kernel_minor(type, card, dev);
|
||||
int minor_unit;
|
||||
snd_minor_t *preg;
|
||||
int cidx = SNDRV_MINOR_OSS_CARD(minor);
|
||||
int track2 = -1;
|
||||
int register1 = -1, register2 = -1;
|
||||
|
||||
if (minor < 0)
|
||||
return minor;
|
||||
preg = (snd_minor_t *)kmalloc(sizeof(snd_minor_t), GFP_KERNEL);
|
||||
if (preg == NULL)
|
||||
return -ENOMEM;
|
||||
*preg = *reg;
|
||||
preg->number = minor;
|
||||
preg->device = dev;
|
||||
down(&sound_oss_mutex);
|
||||
list_add_tail(&preg->list, &snd_oss_minors_hash[cidx]);
|
||||
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
|
||||
switch (minor_unit) {
|
||||
case SNDRV_MINOR_OSS_PCM:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
|
||||
break;
|
||||
case SNDRV_MINOR_OSS_MIDI:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
|
||||
break;
|
||||
case SNDRV_MINOR_OSS_MIDI1:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
|
||||
break;
|
||||
}
|
||||
register1 = register_sound_special(reg->f_ops, minor);
|
||||
if (register1 != minor)
|
||||
goto __end;
|
||||
if (track2 >= 0) {
|
||||
register2 = register_sound_special(reg->f_ops, track2);
|
||||
if (register2 != track2)
|
||||
goto __end;
|
||||
}
|
||||
up(&sound_oss_mutex);
|
||||
return 0;
|
||||
|
||||
__end:
|
||||
if (register2 >= 0)
|
||||
unregister_sound_special(register2);
|
||||
if (register1 >= 0)
|
||||
unregister_sound_special(register1);
|
||||
list_del(&preg->list);
|
||||
up(&sound_oss_mutex);
|
||||
kfree(preg);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int snd_unregister_oss_device(int type, snd_card_t * card, int dev)
|
||||
{
|
||||
int minor = snd_oss_kernel_minor(type, card, dev);
|
||||
int cidx = SNDRV_MINOR_OSS_CARD(minor);
|
||||
int track2 = -1;
|
||||
snd_minor_t *mptr;
|
||||
|
||||
if (minor < 0)
|
||||
return minor;
|
||||
down(&sound_oss_mutex);
|
||||
mptr = snd_oss_minor_search(minor);
|
||||
if (mptr == NULL) {
|
||||
up(&sound_oss_mutex);
|
||||
return -ENOENT;
|
||||
}
|
||||
unregister_sound_special(minor);
|
||||
switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
|
||||
case SNDRV_MINOR_OSS_PCM:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
|
||||
break;
|
||||
case SNDRV_MINOR_OSS_MIDI:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI);
|
||||
break;
|
||||
case SNDRV_MINOR_OSS_MIDI1:
|
||||
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
|
||||
break;
|
||||
}
|
||||
if (track2 >= 0)
|
||||
unregister_sound_special(track2);
|
||||
list_del(&mptr->list);
|
||||
up(&sound_oss_mutex);
|
||||
kfree(mptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* INFO PART
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static snd_info_entry_t *snd_minor_info_oss_entry = NULL;
|
||||
|
||||
static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
|
||||
{
|
||||
int card, dev;
|
||||
struct list_head *list;
|
||||
snd_minor_t *mptr;
|
||||
|
||||
down(&sound_oss_mutex);
|
||||
for (card = 0; card < SNDRV_CARDS; card++) {
|
||||
list_for_each(list, &snd_oss_minors_hash[card]) {
|
||||
mptr = list_entry(list, snd_minor_t, list);
|
||||
dev = SNDRV_MINOR_OSS_DEVICE(mptr->number);
|
||||
if (dev != SNDRV_MINOR_OSS_SNDSTAT &&
|
||||
dev != SNDRV_MINOR_OSS_SEQUENCER &&
|
||||
dev != SNDRV_MINOR_OSS_MUSIC)
|
||||
snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", mptr->number, card, dev, mptr->comment);
|
||||
else
|
||||
snd_iprintf(buffer, "%3i: : %s\n", mptr->number, mptr->comment);
|
||||
}
|
||||
}
|
||||
up(&sound_oss_mutex);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
int __init snd_minor_info_oss_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
snd_info_entry_t *entry;
|
||||
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
|
||||
if (entry) {
|
||||
entry->c.text.read_size = PAGE_SIZE;
|
||||
entry->c.text.read = snd_minor_info_oss_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
}
|
||||
snd_minor_info_oss_entry = entry;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __exit snd_minor_info_oss_done(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
if (snd_minor_info_oss_entry)
|
||||
snd_info_unregister(snd_minor_info_oss_entry);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init snd_oss_init_module(void)
|
||||
{
|
||||
int card;
|
||||
|
||||
for (card = 0; card < SNDRV_CARDS; card++)
|
||||
INIT_LIST_HEAD(&snd_oss_minors_hash[card]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SND_OSSEMUL */
|
||||
1901
sound/core/timer.c
Normal file
1901
sound/core/timer.c
Normal file
File diff suppressed because it is too large
Load Diff
119
sound/core/timer_compat.c
Normal file
119
sound/core/timer_compat.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 32bit -> 64bit ioctl wrapper for timer API
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* This file included from timer.c */
|
||||
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct sndrv_timer_info32 {
|
||||
u32 flags;
|
||||
s32 card;
|
||||
unsigned char id[64];
|
||||
unsigned char name[80];
|
||||
u32 reserved0;
|
||||
u32 resolution;
|
||||
unsigned char reserved[64];
|
||||
};
|
||||
|
||||
static int snd_timer_user_info_compat(struct file *file,
|
||||
struct sndrv_timer_info32 __user *_info)
|
||||
{
|
||||
snd_timer_user_t *tu;
|
||||
struct sndrv_timer_info32 info;
|
||||
snd_timer_t *t;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
t = tu->timeri->timer;
|
||||
snd_assert(t != NULL, return -ENXIO);
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.card = t->card ? t->card->number : -1;
|
||||
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
|
||||
info.flags |= SNDRV_TIMER_FLG_SLAVE;
|
||||
strlcpy(info.id, t->id, sizeof(info.id));
|
||||
strlcpy(info.name, t->name, sizeof(info.name));
|
||||
info.resolution = t->hw.resolution;
|
||||
if (copy_to_user(_info, &info, sizeof(*_info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sndrv_timer_status32 {
|
||||
struct compat_timespec tstamp;
|
||||
u32 resolution;
|
||||
u32 lost;
|
||||
u32 overrun;
|
||||
u32 queue;
|
||||
unsigned char reserved[64];
|
||||
};
|
||||
|
||||
static int snd_timer_user_status_compat(struct file *file,
|
||||
struct sndrv_timer_status32 __user *_status)
|
||||
{
|
||||
snd_timer_user_t *tu;
|
||||
snd_timer_status_t status;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
memset(&status, 0, sizeof(status));
|
||||
status.tstamp = tu->tstamp;
|
||||
status.resolution = snd_timer_resolution(tu->timeri);
|
||||
status.lost = tu->timeri->lost;
|
||||
status.overrun = tu->overrun;
|
||||
spin_lock_irq(&tu->qlock);
|
||||
status.queue = tu->qused;
|
||||
spin_unlock_irq(&tu->qlock);
|
||||
if (copy_to_user(_status, &status, sizeof(status)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
enum {
|
||||
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct sndrv_timer_info32),
|
||||
SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct sndrv_timer_status32),
|
||||
};
|
||||
|
||||
static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = compat_ptr(arg);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_TIMER_IOCTL_PVERSION:
|
||||
case SNDRV_TIMER_IOCTL_TREAD:
|
||||
case SNDRV_TIMER_IOCTL_GINFO:
|
||||
case SNDRV_TIMER_IOCTL_GPARAMS:
|
||||
case SNDRV_TIMER_IOCTL_GSTATUS:
|
||||
case SNDRV_TIMER_IOCTL_SELECT:
|
||||
case SNDRV_TIMER_IOCTL_PARAMS:
|
||||
case SNDRV_TIMER_IOCTL_START:
|
||||
case SNDRV_TIMER_IOCTL_STOP:
|
||||
case SNDRV_TIMER_IOCTL_CONTINUE:
|
||||
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
|
||||
return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
|
||||
case SNDRV_TIMER_IOCTL_INFO32:
|
||||
return snd_timer_user_info_compat(file, argp);
|
||||
case SNDRV_TIMER_IOCTL_STATUS32:
|
||||
return snd_timer_user_status_compat(file, argp);
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
50
sound/core/wrappers.c
Normal file
50
sound/core/wrappers.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Various wrappers
|
||||
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG_MEMORY
|
||||
void *snd_wrapper_kmalloc(size_t size, int flags)
|
||||
{
|
||||
return kmalloc(size, flags);
|
||||
}
|
||||
|
||||
void snd_wrapper_kfree(const void *obj)
|
||||
{
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
void *snd_wrapper_vmalloc(unsigned long size)
|
||||
{
|
||||
return vmalloc(size);
|
||||
}
|
||||
|
||||
void snd_wrapper_vfree(void *obj)
|
||||
{
|
||||
vfree(obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user