aboutsummaryrefslogtreecommitdiff
path: root/sound/drivers/opl3
diff options
context:
space:
mode:
Diffstat (limited to 'sound/drivers/opl3')
-rw-r--r--sound/drivers/opl3/Makefile18
-rw-r--r--sound/drivers/opl3/opl3_lib.c41
-rw-r--r--sound/drivers/opl3/opl3_midi.c116
-rw-r--r--sound/drivers/opl3/opl3_oss.c173
-rw-r--r--sound/drivers/opl3/opl3_seq.c55
-rw-r--r--sound/drivers/opl3/opl3_synth.c199
6 files changed, 350 insertions, 252 deletions
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
index 12059785b5c..7f2c2a10c4e 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -1,22 +1,12 @@
#
# Makefile for ALSA
-# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
-snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o
-ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
-snd-opl3-synth-objs += opl3_oss.o
-endif
-
-#
-# 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)))
+snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
+snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-opl3-synth.o
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index 1e0c76b9acf..f66af5884c4 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Hannu Savolainen 1993-1996,
* Rob Hooft
*
@@ -26,12 +26,13 @@
#include <sound/opl3.h>
#include <asm/io.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/minors.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");
MODULE_LICENSE("GPL");
@@ -139,7 +140,8 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
* If we had an OPL4 chip, opl3->hardware would have been set
* by the OPL4 driver; so we can assume OPL3 here.
*/
- snd_assert(opl3->r_port != 0, return -ENODEV);
+ if (snd_BUG_ON(!opl3->r_port))
+ return -ENODEV;
opl3->hardware = OPL3_HW_OPL3;
}
return 0;
@@ -301,7 +303,7 @@ void snd_opl3_interrupt(struct snd_hwdep * hw)
opl3 = hw->private_data;
status = inb(opl3->l_port);
#if 0
- snd_printk("AdLib IRQ status = 0x%x\n", status);
+ snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status);
#endif
if (!(status & 0x80))
return;
@@ -316,15 +318,19 @@ void snd_opl3_interrupt(struct snd_hwdep * hw)
}
}
+EXPORT_SYMBOL(snd_opl3_interrupt);
+
/*
*/
static int snd_opl3_free(struct snd_opl3 *opl3)
{
- snd_assert(opl3 != NULL, return -ENXIO);
+ if (snd_BUG_ON(!opl3))
+ return -ENXIO;
if (opl3->private_free)
opl3->private_free(opl3);
+ snd_opl3_clear_patches(opl3);
release_and_free_resource(opl3->res_l_port);
release_and_free_resource(opl3->res_r_port);
kfree(opl3);
@@ -358,7 +364,6 @@ int snd_opl3_new(struct snd_card *card,
opl3->hardware = hardware;
spin_lock_init(&opl3->reg_lock);
spin_lock_init(&opl3->timer_lock);
- init_MUTEX(&opl3->access_mutex);
if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
snd_opl3_free(opl3);
@@ -369,6 +374,8 @@ int snd_opl3_new(struct snd_card *card,
return 0;
}
+EXPORT_SYMBOL(snd_opl3_new);
+
int snd_opl3_init(struct snd_opl3 *opl3)
{
if (! opl3->command) {
@@ -393,6 +400,8 @@ int snd_opl3_init(struct snd_opl3 *opl3)
return 0;
}
+EXPORT_SYMBOL(snd_opl3_init);
+
int snd_opl3_create(struct snd_card *card,
unsigned long l_port,
unsigned long r_port,
@@ -451,6 +460,8 @@ int snd_opl3_create(struct snd_card *card,
return 0;
}
+EXPORT_SYMBOL(snd_opl3_create);
+
int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
{
int err;
@@ -468,6 +479,8 @@ int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
return 0;
}
+EXPORT_SYMBOL(snd_opl3_timer_new);
+
int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
int device, int seq_device,
struct snd_hwdep ** rhwdep)
@@ -486,11 +499,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
return err;
}
hw->private_data = opl3;
+ hw->exclusive = 1;
#ifdef CONFIG_SND_OSSEMUL
- if (device == 0) {
+ if (device == 0)
hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
- sprintf(hw->oss_dev, "dmfm%i", card->number);
- }
#endif
strcpy(hw->name, hw->id);
switch (opl3->hardware & OPL3_HW_MASK) {
@@ -511,8 +523,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
/* operators - only ioctl */
hw->ops.open = snd_opl3_open;
hw->ops.ioctl = snd_opl3_ioctl;
+ hw->ops.write = snd_opl3_write;
hw->ops.release = snd_opl3_release;
+ opl3->hwdep = hw;
opl3->seq_dev_num = seq_device;
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
@@ -526,17 +540,8 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
return 0;
}
-EXPORT_SYMBOL(snd_opl3_interrupt);
-EXPORT_SYMBOL(snd_opl3_new);
-EXPORT_SYMBOL(snd_opl3_init);
-EXPORT_SYMBOL(snd_opl3_create);
-EXPORT_SYMBOL(snd_opl3_timer_new);
EXPORT_SYMBOL(snd_opl3_hwdep_new);
-/* opl3_synth.c */
-EXPORT_SYMBOL(snd_opl3_regmap);
-EXPORT_SYMBOL(snd_opl3_reset);
-
/*
* INIT part
*/
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index 1b6f227af37..6c6d09a51f4 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -27,8 +27,10 @@
extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-extern int use_internal_drums;
+extern bool use_internal_drums;
+static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
+ struct snd_midi_channel *chan);
/*
* The next table looks magical, but it certainly is not. Its values have
* been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
@@ -125,7 +127,7 @@ static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
int i;
char *str = "x.24";
- printk("time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
+ printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
for (i = 0; i < opl3->max_voices; i++)
printk("%c", *(str + opl3->voices[i].state + 1));
printk("\n");
@@ -161,7 +163,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
struct best *bp;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -218,7 +220,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
for (i = 0; i < END; i++) {
if (best[i].voice >= 0) {
#ifdef DEBUG_ALLOC
- printk("%s %iop allocation on voice %i\n",
+ printk(KERN_DEBUG "%s %iop allocation on voice %i\n",
alloc_type[i], instr_4op ? 4 : 2,
best[i].voice);
#endif
@@ -242,16 +244,20 @@ void snd_opl3_timer_func(unsigned long data)
int again = 0;
int i;
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+ spin_lock_irqsave(&opl3->voice_lock, flags);
for (i = 0; i < opl3->max_voices; i++) {
struct snd_opl3_voice *vp = &opl3->voices[i];
if (vp->state > 0 && vp->note_off_check) {
if (vp->note_off == jiffies)
- snd_opl3_note_off(opl3, vp->note, 0, vp->chan);
+ snd_opl3_note_off_unsafe(opl3, vp->note, 0,
+ vp->chan);
else
again++;
}
}
+ spin_unlock_irqrestore(&opl3->voice_lock, flags);
+
+ spin_lock_irqsave(&opl3->sys_timer_lock, flags);
if (again) {
opl3->tlist.expires = jiffies + 1; /* invoke again */
add_timer(&opl3->tlist);
@@ -289,8 +295,6 @@ static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
{
struct snd_opl3 *opl3;
- struct snd_seq_instr wanted;
- struct snd_seq_kinstr *kinstr;
int instr_4op;
int voice;
@@ -306,33 +310,33 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
unsigned char voice_offset;
unsigned short opl3_reg;
unsigned char reg_val;
+ unsigned char prg, bank;
int key = note;
unsigned char fnum, blocknum;
int i;
+ struct fm_patch *patch;
struct fm_instrument *fm;
unsigned long flags;
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n",
+ snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n",
chan->number, chan->midi_program, note, vel);
#endif
- wanted.cluster = 0;
- wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, drum voice numbers are notes on drum channel */
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
if (chan->drum_channel) {
/* percussion instruments are located in bank 128 */
- wanted.bank = 128;
- wanted.prg = note;
+ bank = 128;
+ prg = note;
} else {
- wanted.bank = chan->gm_bank_select;
- wanted.prg = chan->midi_program;
+ bank = chan->gm_bank_select;
+ prg = chan->midi_program;
}
} else {
/* Prepare for OSS mode */
@@ -340,8 +344,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
return;
/* OSS instruments are located in bank 127 */
- wanted.bank = 127;
- wanted.prg = chan->midi_program;
+ bank = 127;
+ prg = chan->midi_program;
}
spin_lock_irqsave(&opl3->voice_lock, flags);
@@ -353,15 +357,14 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
__extra_prg:
- kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0);
- if (kinstr == NULL) {
+ patch = snd_opl3_find_patch(opl3, prg, bank, 0);
+ if (!patch) {
spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
- fm = KINSTR_DATA(kinstr);
-
- switch (fm->type) {
+ fm = &patch->inst;
+ switch (patch->type) {
case FM_PATCH_OPL2:
instr_4op = 0;
break;
@@ -371,14 +374,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
break;
}
default:
- snd_seq_instr_free_use(opl3->ilist, kinstr);
spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
-
#ifdef DEBUG_MIDI
- snd_printk(" --> OPL%i instrument: %s\n",
- instr_4op ? 3 : 2, kinstr->name);
+ snd_printk(KERN_DEBUG " --> OPL%i instrument: %s\n",
+ instr_4op ? 3 : 2, patch->name);
#endif
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, allocate voice on free OPL3 channel */
@@ -389,6 +390,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
voice = snd_opl3_oss_map[chan->number];
}
+ if (voice < 0) {
+ spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ return;
+ }
+
if (voice < MAX_OPL2_VOICES) {
/* Left register block for voices 0 .. 8 */
reg_side = OPL3_LEFT;
@@ -436,7 +442,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
#ifdef DEBUG_MIDI
- snd_printk(" --> setting OPL3 connection: 0x%x\n",
+ snd_printk(KERN_DEBUG " --> setting OPL3 connection: 0x%x\n",
opl3->connection_reg);
#endif
/*
@@ -471,7 +477,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* Program the FM voice characteristics */
for (i = 0; i < (instr_4op ? 4 : 2); i++) {
#ifdef DEBUG_MIDI
- snd_printk(" --> programming operator %i\n", i);
+ snd_printk(KERN_DEBUG " --> programming operator %i\n", i);
#endif
op_offset = snd_opl3_regmap[voice_offset][i];
@@ -551,7 +557,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
blocknum |= OPL3_KEYON_BIT;
#ifdef DEBUG_MIDI
- snd_printk(" --> trigger voice %i\n", voice);
+ snd_printk(KERN_DEBUG " --> trigger voice %i\n", voice);
#endif
/* Set OPL3 KEYON_BLOCK register of requested voice */
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
@@ -569,8 +575,6 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* get extra pgm, but avoid possible loops */
extra_prg = (extra_prg) ? 0 : fm->modes;
- snd_seq_instr_free_use(opl3->ilist, kinstr);
-
/* do the bookkeeping */
vp->time = opl3->use_time++;
vp->note = key;
@@ -601,15 +605,15 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* allocate extra program if specified in patch library */
if (extra_prg) {
if (extra_prg > 128) {
- wanted.bank = 128;
+ bank = 128;
/* percussions start at 35 */
- wanted.prg = extra_prg - 128 + 35 - 1;
+ prg = extra_prg - 128 + 35 - 1;
} else {
- wanted.bank = 0;
- wanted.prg = extra_prg - 1;
+ bank = 0;
+ prg = extra_prg - 1;
}
#ifdef DEBUG_MIDI
- snd_printk(" *** allocating extra program\n");
+ snd_printk(KERN_DEBUG " *** allocating extra program\n");
#endif
goto __extra_prg;
}
@@ -624,7 +628,8 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
struct snd_opl3_voice *vp, *vp2;
- snd_assert(voice < MAX_OPL3_VOICES, return);
+ if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+ return;
vp = &opl3->voices[voice];
if (voice < MAX_OPL2_VOICES) {
@@ -639,7 +644,7 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
/* kill voice */
#ifdef DEBUG_MIDI
- snd_printk(" --> kill voice %i\n", voice);
+ snd_printk(KERN_DEBUG " --> kill voice %i\n", voice);
#endif
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
/* clear Key ON bit */
@@ -664,28 +669,24 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
/*
* Release a note in response to a midi note off.
*/
-void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
+static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
+ struct snd_midi_channel *chan)
{
struct snd_opl3 *opl3;
int voice;
struct snd_opl3_voice *vp;
- unsigned long flags;
-
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Note off, ch %i, inst %i, note %i\n",
+ snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n",
chan->number, chan->midi_program, note);
#endif
- spin_lock_irqsave(&opl3->voice_lock, flags);
-
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
if (chan->drum_channel && use_internal_drums) {
snd_opl3_drum_switch(opl3, note, vel, 0, chan);
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
/* this loop will hopefully kill all extra voices, because
@@ -703,6 +704,16 @@ void snd_opl3_note_off(void *p, int note, int vel, struct snd_midi_channel *chan
snd_opl3_kill_voice(opl3, voice);
}
}
+}
+
+void snd_opl3_note_off(void *p, int note, int vel,
+ struct snd_midi_channel *chan)
+{
+ struct snd_opl3 *opl3 = p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&opl3->voice_lock, flags);
+ snd_opl3_note_off_unsafe(p, note, vel, chan);
spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
@@ -715,7 +726,7 @@ void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *cha
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Key pressure, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -729,7 +740,7 @@ void snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan)
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Terminate note, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -744,7 +755,8 @@ static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
struct snd_opl3_voice *vp;
- snd_assert(voice < MAX_OPL3_VOICES, return);
+ if (snd_BUG_ON(voice >= MAX_OPL3_VOICES))
+ return;
vp = &opl3->voices[voice];
if (vp->chan == NULL)
@@ -808,7 +820,7 @@ static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *
}
/*
- * Deal with a controler type event. This includes all types of
+ * Deal with a controller type event. This includes all types of
* control events, not just the midi controllers
*/
void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
@@ -817,7 +829,7 @@ void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
type, chan->number, chan->midi_program);
#endif
@@ -854,7 +866,7 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("NRPN, ch#: %i, inst#: %i\n",
+ snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n",
chan->number, chan->midi_program);
#endif
}
@@ -869,6 +881,6 @@ void snd_opl3_sysex(void *p, unsigned char *buf, int len,
opl3 = p;
#ifdef DEBUG_MIDI
- snd_printk("SYSEX\n");
+ snd_printk(KERN_DEBUG "SYSEX\n");
#endif
}
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index 31f1f2e25aa..c1cb249acfa 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -18,8 +18,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/export.h>
#include "opl3_voice.h"
-#include <linux/slab.h>
static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg);
@@ -100,12 +100,15 @@ static int snd_opl3_oss_create_port(struct snd_opl3 * opl3)
SNDRV_SEQ_PORT_CAP_WRITE,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
voices, voices,
name);
if (opl3->oss_chset->port < 0) {
+ int port;
+ port = opl3->oss_chset->port;
snd_midi_channel_free_set(opl3->oss_chset);
- return opl3->oss_chset->port;
+ return port;
}
return 0;
}
@@ -136,17 +139,17 @@ void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name)
arg->oper = oss_callback;
arg->private_data = opl3;
- snd_opl3_oss_create_port(opl3);
-
- /* register to OSS synth table */
- snd_device_register(opl3->card, dev);
+ if (snd_opl3_oss_create_port(opl3)) {
+ /* register to OSS synth table */
+ snd_device_register(opl3->card, dev);
+ }
}
/* unregister */
void snd_opl3_free_seq_oss(struct snd_opl3 *opl3)
{
if (opl3->oss_seq_dev) {
- snd_device_free(opl3->card, opl3->oss_seq_dev);
+ /* The instance should have been released in prior */
opl3->oss_seq_dev = NULL;
}
}
@@ -159,7 +162,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
struct snd_opl3 *opl3 = closure;
int err;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
if ((err = snd_opl3_synth_setup(opl3)) < 0)
return err;
@@ -181,7 +185,8 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
snd_opl3_synth_cleanup(opl3);
@@ -192,17 +197,6 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
/* load patch */
-/* offsets for SBI params */
-#define AM_VIB 0
-#define KSL_LEVEL 2
-#define ATTACK_DECAY 4
-#define SUSTAIN_RELEASE 6
-#define WAVE_SELECT 8
-
-/* offset for SBI instrument */
-#define CONNECTION 10
-#define OFFSET_4OP 11
-
/* from sound_config.h */
#define SBFM_MAXINSTR 256
@@ -210,112 +204,43 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
const char __user *buf, int offs, int count)
{
struct snd_opl3 *opl3;
- int err = -EINVAL;
+ struct sbi_instrument sbi;
+ char name[32];
+ int err, type;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
- if ((format == FM_PATCH) || (format == OPL3_PATCH)) {
- struct sbi_instrument sbi;
+ if (format == FM_PATCH)
+ type = FM_PATCH_OPL2;
+ else if (format == OPL3_PATCH)
+ type = FM_PATCH_OPL3;
+ else
+ return -EINVAL;
- size_t size;
- struct snd_seq_instr_header *put;
- struct snd_seq_instr_data *data;
- struct fm_xinstrument *xinstr;
+ if (count < (int)sizeof(sbi)) {
+ snd_printk(KERN_ERR "FM Error: Patch record too short\n");
+ return -EINVAL;
+ }
+ if (copy_from_user(&sbi, buf, sizeof(sbi)))
+ return -EFAULT;
- struct snd_seq_event ev;
- int i;
+ if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
+ snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
+ sbi.channel);
+ return -EINVAL;
+ }
- mm_segment_t fs;
+ memset(name, 0, sizeof(name));
+ sprintf(name, "Chan%d", sbi.channel);
- if (count < (int)sizeof(sbi)) {
- snd_printk("FM Error: Patch record too short\n");
- return -EINVAL;
- }
- if (copy_from_user(&sbi, buf, sizeof(sbi)))
- return -EFAULT;
+ err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
+ sbi.operators);
+ if (err < 0)
+ return err;
- if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
- snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel);
- return -EINVAL;
- }
-
- size = sizeof(*put) + sizeof(struct fm_xinstrument);
- put = kzalloc(size, GFP_KERNEL);
- if (put == NULL)
- return -ENOMEM;
- /* build header */
- data = &put->data;
- data->type = SNDRV_SEQ_INSTR_ATYPE_DATA;
- strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3);
- /* build data section */
- xinstr = (struct fm_xinstrument *)(data + 1);
- xinstr->stype = FM_STRU_INSTR;
-
- for (i = 0; i < 2; i++) {
- xinstr->op[i].am_vib = sbi.operators[AM_VIB + i];
- xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i];
- xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i];
- xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i];
- xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i];
- }
- xinstr->feedback_connection[0] = sbi.operators[CONNECTION];
-
- if (format == OPL3_PATCH) {
- xinstr->type = FM_PATCH_OPL3;
- for (i = 0; i < 2; i++) {
- xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i];
- xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i];
- xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i];
- xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i];
- xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i];
- }
- xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION];
- } else {
- xinstr->type = FM_PATCH_OPL2;
- }
-
- put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
- put->id.instr.bank = 127;
- put->id.instr.prg = sbi.channel;
- put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE;
-
- memset (&ev, 0, sizeof(ev));
- ev.source.client = SNDRV_SEQ_CLIENT_OSS;
- ev.dest = arg->addr;
-
- ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR;
- ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
-
- fs = snd_enter_user();
- __again:
- ev.type = SNDRV_SEQ_EVENT_INSTR_PUT;
- ev.data.ext.len = size;
- ev.data.ext.ptr = put;
-
- err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
- opl3->seq_client, 0, 0);
- if (err == -EBUSY) {
- struct snd_seq_instr_header remove;
-
- memset (&remove, 0, sizeof(remove));
- remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE;
- remove.id.instr = put->id.instr;
-
- /* remove instrument */
- ev.type = SNDRV_SEQ_EVENT_INSTR_FREE;
- ev.data.ext.len = sizeof(remove);
- ev.data.ext.ptr = &remove;
-
- snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev,
- opl3->seq_client, 0, 0);
- goto __again;
- }
- snd_leave_user(fs);
-
- kfree(put);
- }
- return err;
+ return sizeof(sbi);
}
/* ioctl */
@@ -324,11 +249,14 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
- snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+ snd_printk(KERN_ERR "OPL3: "
+ "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
+ "Fix the program.\n");
return -EINVAL;
case SNDCTL_SYNTH_MEMAVL:
@@ -349,7 +277,8 @@ static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
{
struct snd_opl3 *opl3;
- snd_assert(arg != NULL, return -ENXIO);
+ if (snd_BUG_ON(!arg))
+ return -ENXIO;
opl3 = arg->private_data;
return 0;
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
index c4ead790008..68399538e43 100644
--- a/sound/drivers/opl3/opl3_seq.c
+++ b/sound/drivers/opl3/opl3_seq.c
@@ -25,13 +25,14 @@
#include "opl3_voice.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
-int use_internal_drums = 0;
+bool use_internal_drums = 0;
module_param(use_internal_drums, bool, 0444);
MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
@@ -51,14 +52,15 @@ void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
int snd_opl3_synth_setup(struct snd_opl3 * opl3)
{
int idx;
+ struct snd_hwdep *hwdep = opl3->hwdep;
- down(&opl3->access_mutex);
- if (opl3->used) {
- up(&opl3->access_mutex);
+ mutex_lock(&hwdep->open_mutex);
+ if (hwdep->used) {
+ mutex_unlock(&hwdep->open_mutex);
return -EBUSY;
}
- opl3->used++;
- up(&opl3->access_mutex);
+ hwdep->used++;
+ mutex_unlock(&hwdep->open_mutex);
snd_opl3_reset(opl3);
@@ -81,6 +83,7 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
{
unsigned long flags;
+ struct snd_hwdep *hwdep;
/* Stop system timer */
spin_lock_irqsave(&opl3->sys_timer_lock, flags);
@@ -91,9 +94,11 @@ void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
snd_opl3_reset(opl3);
- down(&opl3->access_mutex);
- opl3->used--;
- up(&opl3->access_mutex);
+ hwdep = opl3->hwdep;
+ mutex_lock(&hwdep->open_mutex);
+ hwdep->used--;
+ mutex_unlock(&hwdep->open_mutex);
+ wake_up(&hwdep->open_wait);
}
static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
@@ -152,15 +157,7 @@ static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
{
struct snd_opl3 *opl3 = private_data;
- if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN &&
- ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) {
- if (direct) {
- snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev,
- opl3->seq_client, atomic, hop);
- }
- } else {
- snd_midi_process_event(&opl3_ops, ev, opl3->chset);
- }
+ snd_midi_process_event(&opl3_ops, ev, opl3->chset);
return 0;
}
@@ -203,12 +200,16 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
SNDRV_SEQ_PORT_TYPE_MIDI_GM |
- SNDRV_SEQ_PORT_TYPE_SYNTH,
+ SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
16, voices,
name);
if (opl3->chset->port < 0) {
+ int port;
+ port = opl3->chset->port;
snd_midi_channel_free_set(opl3->chset);
- return opl3->chset->port;
+ return port;
}
return 0;
}
@@ -218,7 +219,7 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
{
struct snd_opl3 *opl3;
- int client;
+ int client, err;
char name[32];
int opl_ver;
@@ -239,17 +240,11 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
if (client < 0)
return client;
- snd_opl3_synth_create_port(opl3);
-
- /* initialize instrument list */
- opl3->ilist = snd_seq_instr_list_new();
- if (opl3->ilist == NULL) {
+ if ((err = snd_opl3_synth_create_port(opl3)) < 0) {
snd_seq_delete_kernel_client(client);
opl3->seq_client = -1;
- return -ENOMEM;
+ return err;
}
- opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT;
- snd_seq_fm_init(&opl3->fm_ops, NULL);
/* setup system timer */
init_timer(&opl3->tlist);
@@ -279,8 +274,6 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev)
snd_seq_delete_kernel_client(opl3->seq_client);
opl3->seq_client = -1;
}
- if (opl3->ilist)
- snd_seq_instr_list_free(&opl3->ilist);
return 0;
}
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index 3534a0e3342..ddcc1a325a6 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -19,9 +19,15 @@
*
*/
+#include <linux/slab.h>
+#include <linux/export.h>
#include <sound/opl3.h>
#include <sound/asound_fm.h>
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+#define OPL3_SUPPORT_SYNTH
+#endif
+
/*
* There is 18 possible 2 OP voices
* (9 in the left and 9 in the right).
@@ -58,6 +64,8 @@ char snd_opl3_regmap[MAX_OPL2_VOICES][4] =
{ 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */
};
+EXPORT_SYMBOL(snd_opl3_regmap);
+
/*
* prototypes
*/
@@ -74,16 +82,6 @@ static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection);
*/
int snd_opl3_open(struct snd_hwdep * hw, struct file *file)
{
- struct snd_opl3 *opl3 = hw->private_data;
-
- down(&opl3->access_mutex);
- if (opl3->used) {
- up(&opl3->access_mutex);
- return -EAGAIN;
- }
- opl3->used++;
- up(&opl3->access_mutex);
-
return 0;
}
@@ -96,7 +94,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
struct snd_opl3 *opl3 = hw->private_data;
void __user *argp = (void __user *)arg;
- snd_assert(opl3 != NULL, return -EINVAL);
+ if (snd_BUG_ON(!opl3))
+ return -EINVAL;
switch (cmd) {
/* get information */
@@ -163,9 +162,15 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
#endif
return snd_opl3_set_connection(opl3, (int) arg);
+#ifdef OPL3_SUPPORT_SYNTH
+ case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES:
+ snd_opl3_clear_patches(opl3);
+ return 0;
+#endif
+
#ifdef CONFIG_SND_DEBUG
default:
- snd_printk("unknown IOCTL: 0x%x\n", cmd);
+ snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd);
#endif
}
return -ENOTTY;
@@ -179,12 +184,174 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file)
struct snd_opl3 *opl3 = hw->private_data;
snd_opl3_reset(opl3);
- down(&opl3->access_mutex);
- opl3->used--;
- up(&opl3->access_mutex);
+ return 0;
+}
+
+#ifdef OPL3_SUPPORT_SYNTH
+/*
+ * write the device - load patches
+ */
+long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_opl3 *opl3 = hw->private_data;
+ long result = 0;
+ int err = 0;
+ struct sbi_patch inst;
+
+ while (count >= sizeof(inst)) {
+ unsigned char type;
+ if (copy_from_user(&inst, buf, sizeof(inst)))
+ return -EFAULT;
+ if (!memcmp(inst.key, FM_KEY_SBI, 4) ||
+ !memcmp(inst.key, FM_KEY_2OP, 4))
+ type = FM_PATCH_OPL2;
+ else if (!memcmp(inst.key, FM_KEY_4OP, 4))
+ type = FM_PATCH_OPL3;
+ else /* invalid type */
+ break;
+ err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type,
+ inst.name, inst.extension,
+ inst.data);
+ if (err < 0)
+ break;
+ result += sizeof(inst);
+ count -= sizeof(inst);
+ }
+ return result > 0 ? result : err;
+}
+
+
+/*
+ * Patch management
+ */
+
+/* offsets for SBI params */
+#define AM_VIB 0
+#define KSL_LEVEL 2
+#define ATTACK_DECAY 4
+#define SUSTAIN_RELEASE 6
+#define WAVE_SELECT 8
+
+/* offset for SBI instrument */
+#define CONNECTION 10
+#define OFFSET_4OP 11
+
+/*
+ * load a patch, obviously.
+ *
+ * loaded on the given program and bank numbers with the given type
+ * (FM_PATCH_OPLx).
+ * data is the pointer of SBI record _without_ header (key and name).
+ * name is the name string of the patch.
+ * ext is the extension data of 7 bytes long (stored in name of SBI
+ * data up to offset 25), or NULL to skip.
+ * return 0 if successful or a negative error code.
+ */
+int snd_opl3_load_patch(struct snd_opl3 *opl3,
+ int prog, int bank, int type,
+ const char *name,
+ const unsigned char *ext,
+ const unsigned char *data)
+{
+ struct fm_patch *patch;
+ int i;
+
+ patch = snd_opl3_find_patch(opl3, prog, bank, 1);
+ if (!patch)
+ return -ENOMEM;
+
+ patch->type = type;
+
+ for (i = 0; i < 2; i++) {
+ patch->inst.op[i].am_vib = data[AM_VIB + i];
+ patch->inst.op[i].ksl_level = data[KSL_LEVEL + i];
+ patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i];
+ patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i];
+ patch->inst.op[i].wave_select = data[WAVE_SELECT + i];
+ }
+ patch->inst.feedback_connection[0] = data[CONNECTION];
+
+ if (type == FM_PATCH_OPL3) {
+ for (i = 0; i < 2; i++) {
+ patch->inst.op[i+2].am_vib =
+ data[OFFSET_4OP + AM_VIB + i];
+ patch->inst.op[i+2].ksl_level =
+ data[OFFSET_4OP + KSL_LEVEL + i];
+ patch->inst.op[i+2].attack_decay =
+ data[OFFSET_4OP + ATTACK_DECAY + i];
+ patch->inst.op[i+2].sustain_release =
+ data[OFFSET_4OP + SUSTAIN_RELEASE + i];
+ patch->inst.op[i+2].wave_select =
+ data[OFFSET_4OP + WAVE_SELECT + i];
+ }
+ patch->inst.feedback_connection[1] =
+ data[OFFSET_4OP + CONNECTION];
+ }
+
+ if (ext) {
+ patch->inst.echo_delay = ext[0];
+ patch->inst.echo_atten = ext[1];
+ patch->inst.chorus_spread = ext[2];
+ patch->inst.trnsps = ext[3];
+ patch->inst.fix_dur = ext[4];
+ patch->inst.modes = ext[5];
+ patch->inst.fix_key = ext[6];
+ }
+
+ if (name)
+ strlcpy(patch->name, name, sizeof(patch->name));
return 0;
}
+EXPORT_SYMBOL(snd_opl3_load_patch);
+
+/*
+ * find a patch with the given program and bank numbers, returns its pointer
+ * if no matching patch is found and create_patch is set, it creates a
+ * new patch object.
+ */
+struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
+ int create_patch)
+{
+ /* pretty dumb hash key */
+ unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE;
+ struct fm_patch *patch;
+
+ for (patch = opl3->patch_table[key]; patch; patch = patch->next) {
+ if (patch->prog == prog && patch->bank == bank)
+ return patch;
+ }
+ if (!create_patch)
+ return NULL;
+
+ patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+ if (!patch)
+ return NULL;
+ patch->prog = prog;
+ patch->bank = bank;
+ patch->next = opl3->patch_table[key];
+ opl3->patch_table[key] = patch;
+ return patch;
+}
+EXPORT_SYMBOL(snd_opl3_find_patch);
+
+/*
+ * Clear all patches of the given OPL3 instance
+ */
+void snd_opl3_clear_patches(struct snd_opl3 *opl3)
+{
+ int i;
+ for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) {
+ struct fm_patch *patch, *next;
+ for (patch = opl3->patch_table[i]; patch; patch = next) {
+ next = patch->next;
+ kfree(patch);
+ }
+ }
+ memset(opl3->patch_table, 0, sizeof(opl3->patch_table));
+}
+#endif /* OPL3_SUPPORT_SYNTH */
/* ------------------------------ */
@@ -228,6 +395,7 @@ void snd_opl3_reset(struct snd_opl3 * opl3)
opl3->rhythm = 0;
}
+EXPORT_SYMBOL(snd_opl3_reset);
static int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note)
{
@@ -445,3 +613,4 @@ static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection)
return 0;
}
+