Hi,
one of big pending patches on my local tree is the removal of seq-instr interface. The only real user of instrument layer is OPL3 patch loader, and I'd like to move this to hwdep interface.
The patch below is to move the OPL3 patch loading to hwdep device. Two more patches will follow in other mails for alsa-lib addition and sbiload change.
Can someone test whether these patches work? Unfortunately, I have no hardware with OPL3 at this moment around here.
Thanks,
Takashi
diff -r 88d273f360dd drivers/opl3/opl3_lib.c --- a/drivers/opl3/opl3_lib.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_lib.c Fri Oct 26 16:09:56 2007 +0200 @@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_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 * /* 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; diff -r 88d273f360dd drivers/opl3/opl3_midi.c --- a/drivers/opl3/opl3_midi.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_midi.c Fri Oct 26 16:09:56 2007 +0200 @@ -289,8 +289,6 @@ void snd_opl3_note_on(void *p, int note, 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, 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, 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, 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, }
__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, 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, /* 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, /* 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"); diff -r 88d273f360dd drivers/opl3/opl3_oss.c --- a/drivers/opl3/opl3_oss.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_oss.c Fri Oct 26 16:09:56 2007 +0200 @@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct
/* 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,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(s 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; + 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("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("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 */ diff -r 88d273f360dd drivers/opl3/opl3_seq.c --- a/drivers/opl3/opl3_seq.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_seq.c Fri Oct 26 16:09:56 2007 +0200 @@ -152,15 +152,7 @@ static int snd_opl3_synth_event_input(st { 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; }
@@ -249,16 +241,6 @@ static int snd_opl3_seq_new_device(struc return err; }
- /* initialize instrument list */ - opl3->ilist = snd_seq_instr_list_new(); - if (opl3->ilist == NULL) { - snd_seq_delete_kernel_client(client); - opl3->seq_client = -1; - return -ENOMEM; - } - opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - snd_seq_fm_init(&opl3->fm_ops, NULL); - /* setup system timer */ init_timer(&opl3->tlist); opl3->tlist.function = snd_opl3_timer_func; @@ -287,8 +269,6 @@ static int snd_opl3_seq_delete_device(st 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 -r 88d273f360dd drivers/opl3/opl3_synth.c --- a/drivers/opl3/opl3_synth.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_synth.c Fri Oct 26 16:09:56 2007 +0200 @@ -186,6 +186,170 @@ int snd_opl3_release(struct snd_hwdep * mutex_unlock(&opl3->access_mutex);
return 0; +} + +/* + * 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)); }
/* ------------------------------ */ diff -r 88d273f360dd include/asound_fm.h --- a/include/asound_fm.h Fri Oct 26 15:48:22 2007 +0200 +++ b/include/asound_fm.h Fri Oct 26 16:09:56 2007 +0200 @@ -112,4 +112,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 */ diff -r 88d273f360dd include/opl3.h --- a/include/opl3.h Fri Oct 26 15:48:22 2007 +0200 +++ b/include/opl3.h Fri Oct 26 16:09:56 2007 +0200 @@ -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 @@ -240,6 +240,47 @@ struct snd_opl3; 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 */ struct snd_opl3_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_open(struct snd_hwdep * hw, 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 */