Commit 224a0332 authored by Takashi Iwai's avatar Takashi Iwai Committed by Jaroslav Kysela
Browse files

[ALSA] opl3 - Use hwdep for patch loading



Use the hwdep device for loading OPL2/3 patch data instead of the
messy sequencer instrument layer.
Due to this change, the sbiload program should be updated, too.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent ceac4bf3
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@ struct snd_dm_fm_params {
#define SNDRV_DM_FM_IOCTL_SET_MODE	_IOW('H', 0x25, int)
/* for OPL3 only */
#define SNDRV_DM_FM_IOCTL_SET_CONNECTION	_IOW('H', 0x26, int)
/* SBI patch management */
#define SNDRV_DM_FM_IOCTL_CLEAR_PATCHES	_IO ('H', 0x40)

#define SNDRV_DM_FM_OSS_IOCTL_RESET		0x20
#define SNDRV_DM_FM_OSS_IOCTL_PLAY_NOTE		0x21
@@ -112,4 +114,21 @@ struct snd_dm_fm_params {
#define SNDRV_DM_FM_OSS_IOCTL_SET_MODE		0x24
#define SNDRV_DM_FM_OSS_IOCTL_SET_OPL		0x25

/*
 * Patch Record - fixed size for write
 */

#define FM_KEY_SBI	"SBI\032"
#define FM_KEY_2OP	"2OP\032"
#define FM_KEY_4OP	"4OP\032"

struct sbi_patch {
	unsigned char prog;
	unsigned char bank;
	char key[4];
	char name[25];
	char extension[7];
	unsigned char data[32];
};

#endif /* __SOUND_ASOUND_FM_H */
+55 −3
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@
#include "seq_oss_legacy.h"
#endif
#include "seq_device.h"
#include "ainstr_fm.h"
#include "asound_fm.h"

/*
 *    Register numbers for the global registers
@@ -239,6 +239,47 @@

struct snd_opl3;

/*
 * Instrument record, aka "Patch"
 */

/* FM operator */
struct fm_operator {
	unsigned char am_vib;
	unsigned char ksl_level;
	unsigned char attack_decay;
	unsigned char sustain_release;
	unsigned char wave_select;
} __attribute__((packed));

/* Instrument data */
struct fm_instrument {
	struct fm_operator op[4];
	unsigned char feedback_connection[2];
	unsigned char echo_delay;
	unsigned char echo_atten;
	unsigned char chorus_spread;
	unsigned char trnsps;
	unsigned char fix_dur;
	unsigned char modes;
	unsigned char fix_key;
};

/* type */
#define FM_PATCH_OPL2	0x01		/* OPL2 2 operators FM instrument */
#define FM_PATCH_OPL3	0x02		/* OPL3 4 operators FM instrument */

/* Instrument record */
struct fm_patch {
	unsigned char prog;
	unsigned char bank;
	unsigned char type;
	struct fm_instrument inst;
	char name[24];
	struct fm_patch *next;
};


/*
 * A structure to keep track of each hardware voice
 */
@@ -297,8 +338,8 @@ struct snd_opl3 {
	struct snd_midi_channel_set * oss_chset;
#endif
 
	struct snd_seq_kinstr_ops fm_ops;
	struct snd_seq_kinstr_list *ilist;
#define OPL3_PATCH_HASH_SIZE	32
	struct fm_patch *patch_table[OPL3_PATCH_HASH_SIZE];

	struct snd_opl3_voice voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */
	int use_time;			/* allocation counter */
@@ -333,8 +374,19 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, int device, int seq_device,
int snd_opl3_open(struct snd_hwdep * hw, struct file *file);
int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
		   unsigned int cmd, unsigned long arg);
long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count,
		    loff_t *offset);
int snd_opl3_release(struct snd_hwdep * hw, struct file *file);

void snd_opl3_reset(struct snd_opl3 * opl3);

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 *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank,
				     int create_patch);
void snd_opl3_clear_patches(struct snd_opl3 *opl3);

#endif /* __SOUND_OPL3_H */
+2 −0
Original line number Diff line number Diff line
@@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 *opl3)
	snd_assert(opl3 != NULL, 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);
@@ -521,6 +522,7 @@ 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->seq_dev_num = seq_device;
+17 −24
Original line number Diff line number Diff line
@@ -289,8 +289,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,11 +304,13 @@ 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;

@@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
	snd_printk("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 +338,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 +351,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 +368,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);
		   instr_4op ? 3 : 2, patch->name);
#endif
	/* in SYNTH mode, application takes care of voices */
	/* in SEQ mode, allocate voice on free OPL3 channel */
@@ -569,8 +564,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,12 +594,12 @@ 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");
+27 −108
Original line number Diff line number Diff line
@@ -195,17 +195,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

@@ -213,23 +202,19 @@ 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);
	opl3 = arg->private_data;

	if ((format == FM_PATCH) || (format == OPL3_PATCH)) {
		struct sbi_instrument sbi;

		size_t size;
		struct snd_seq_instr_header *put;
		struct snd_seq_instr_data *data;
		struct fm_xinstrument *xinstr;

		struct snd_seq_event ev;
		int i;

		mm_segment_t fs;
	if (format == FM_PATCH)
		type = FM_PATCH_OPL2;
	else if (format == OPL3_PATCH)
		type = FM_PATCH_OPL3;
	else
		return -EINVAL;

	if (count < (int)sizeof(sbi)) {
		snd_printk("FM Error: Patch record too short\n");
@@ -239,86 +224,20 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
		return -EFAULT;

	if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
			snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel);
		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;
		}
	memset(name, 0, sizeof(name));
	sprintf(name, "Chan%d", sbi.channel);

		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);
	}
	err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
				  sbi.operators);
	if (err < 0)
		return err;

	return sizeof(sbi);
}

/* ioctl */
Loading