diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-10-30 06:49:22 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-01-31 11:29:13 -0500 |
commit | 224a033252bba46c5c8b5df625f5e781ca138f48 (patch) | |
tree | 11bb0ad9a3bab736091c73bb46b79d42ee0ff34d /sound | |
parent | ceac4bf34e14d9040d16b35fd97a92d6e951ccf4 (diff) |
[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: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/drivers/opl3/opl3_lib.c | 2 | ||||
-rw-r--r-- | sound/drivers/opl3/opl3_midi.c | 41 | ||||
-rw-r--r-- | sound/drivers/opl3/opl3_oss.c | 135 | ||||
-rw-r--r-- | sound/drivers/opl3/opl3_seq.c | 22 | ||||
-rw-r--r-- | sound/drivers/opl3/opl3_synth.c | 168 |
5 files changed, 215 insertions, 153 deletions
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index a2b9ce060295..a657da922b4d 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c | |||
@@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 *opl3) | |||
327 | snd_assert(opl3 != NULL, return -ENXIO); | 327 | snd_assert(opl3 != NULL, return -ENXIO); |
328 | if (opl3->private_free) | 328 | if (opl3->private_free) |
329 | opl3->private_free(opl3); | 329 | opl3->private_free(opl3); |
330 | snd_opl3_clear_patches(opl3); | ||
330 | release_and_free_resource(opl3->res_l_port); | 331 | release_and_free_resource(opl3->res_l_port); |
331 | release_and_free_resource(opl3->res_r_port); | 332 | release_and_free_resource(opl3->res_r_port); |
332 | kfree(opl3); | 333 | kfree(opl3); |
@@ -521,6 +522,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, | |||
521 | /* operators - only ioctl */ | 522 | /* operators - only ioctl */ |
522 | hw->ops.open = snd_opl3_open; | 523 | hw->ops.open = snd_opl3_open; |
523 | hw->ops.ioctl = snd_opl3_ioctl; | 524 | hw->ops.ioctl = snd_opl3_ioctl; |
525 | hw->ops.write = snd_opl3_write; | ||
524 | hw->ops.release = snd_opl3_release; | 526 | hw->ops.release = snd_opl3_release; |
525 | 527 | ||
526 | opl3->seq_dev_num = seq_device; | 528 | opl3->seq_dev_num = seq_device; |
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 3557b6e20eb5..cebcb8b78acb 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c | |||
@@ -289,8 +289,6 @@ static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { | |||
289 | void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | 289 | void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) |
290 | { | 290 | { |
291 | struct snd_opl3 *opl3; | 291 | struct snd_opl3 *opl3; |
292 | struct snd_seq_instr wanted; | ||
293 | struct snd_seq_kinstr *kinstr; | ||
294 | int instr_4op; | 292 | int instr_4op; |
295 | 293 | ||
296 | int voice; | 294 | int voice; |
@@ -306,11 +304,13 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | |||
306 | unsigned char voice_offset; | 304 | unsigned char voice_offset; |
307 | unsigned short opl3_reg; | 305 | unsigned short opl3_reg; |
308 | unsigned char reg_val; | 306 | unsigned char reg_val; |
307 | unsigned char prg, bank; | ||
309 | 308 | ||
310 | int key = note; | 309 | int key = note; |
311 | unsigned char fnum, blocknum; | 310 | unsigned char fnum, blocknum; |
312 | int i; | 311 | int i; |
313 | 312 | ||
313 | struct fm_patch *patch; | ||
314 | struct fm_instrument *fm; | 314 | struct fm_instrument *fm; |
315 | unsigned long flags; | 315 | unsigned long flags; |
316 | 316 | ||
@@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | |||
320 | snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", | 320 | snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", |
321 | chan->number, chan->midi_program, note, vel); | 321 | chan->number, chan->midi_program, note, vel); |
322 | #endif | 322 | #endif |
323 | wanted.cluster = 0; | ||
324 | wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; | ||
325 | 323 | ||
326 | /* in SYNTH mode, application takes care of voices */ | 324 | /* in SYNTH mode, application takes care of voices */ |
327 | /* in SEQ mode, drum voice numbers are notes on drum channel */ | 325 | /* in SEQ mode, drum voice numbers are notes on drum channel */ |
328 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | 326 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { |
329 | if (chan->drum_channel) { | 327 | if (chan->drum_channel) { |
330 | /* percussion instruments are located in bank 128 */ | 328 | /* percussion instruments are located in bank 128 */ |
331 | wanted.bank = 128; | 329 | bank = 128; |
332 | wanted.prg = note; | 330 | prg = note; |
333 | } else { | 331 | } else { |
334 | wanted.bank = chan->gm_bank_select; | 332 | bank = chan->gm_bank_select; |
335 | wanted.prg = chan->midi_program; | 333 | prg = chan->midi_program; |
336 | } | 334 | } |
337 | } else { | 335 | } else { |
338 | /* Prepare for OSS mode */ | 336 | /* Prepare for OSS mode */ |
@@ -340,8 +338,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | |||
340 | return; | 338 | return; |
341 | 339 | ||
342 | /* OSS instruments are located in bank 127 */ | 340 | /* OSS instruments are located in bank 127 */ |
343 | wanted.bank = 127; | 341 | bank = 127; |
344 | wanted.prg = chan->midi_program; | 342 | prg = chan->midi_program; |
345 | } | 343 | } |
346 | 344 | ||
347 | spin_lock_irqsave(&opl3->voice_lock, flags); | 345 | 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) | |||
353 | } | 351 | } |
354 | 352 | ||
355 | __extra_prg: | 353 | __extra_prg: |
356 | kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); | 354 | patch = snd_opl3_find_patch(opl3, prg, bank, 0); |
357 | if (kinstr == NULL) { | 355 | if (!patch) { |
358 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 356 | spin_unlock_irqrestore(&opl3->voice_lock, flags); |
359 | return; | 357 | return; |
360 | } | 358 | } |
361 | 359 | ||
362 | fm = KINSTR_DATA(kinstr); | 360 | fm = &patch->inst; |
363 | 361 | switch (patch->type) { | |
364 | switch (fm->type) { | ||
365 | case FM_PATCH_OPL2: | 362 | case FM_PATCH_OPL2: |
366 | instr_4op = 0; | 363 | instr_4op = 0; |
367 | break; | 364 | break; |
@@ -371,14 +368,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | |||
371 | break; | 368 | break; |
372 | } | 369 | } |
373 | default: | 370 | default: |
374 | snd_seq_instr_free_use(opl3->ilist, kinstr); | ||
375 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 371 | spin_unlock_irqrestore(&opl3->voice_lock, flags); |
376 | return; | 372 | return; |
377 | } | 373 | } |
378 | |||
379 | #ifdef DEBUG_MIDI | 374 | #ifdef DEBUG_MIDI |
380 | snd_printk(" --> OPL%i instrument: %s\n", | 375 | snd_printk(" --> OPL%i instrument: %s\n", |
381 | instr_4op ? 3 : 2, kinstr->name); | 376 | instr_4op ? 3 : 2, patch->name); |
382 | #endif | 377 | #endif |
383 | /* in SYNTH mode, application takes care of voices */ | 378 | /* in SYNTH mode, application takes care of voices */ |
384 | /* in SEQ mode, allocate voice on free OPL3 channel */ | 379 | /* 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) | |||
569 | /* get extra pgm, but avoid possible loops */ | 564 | /* get extra pgm, but avoid possible loops */ |
570 | extra_prg = (extra_prg) ? 0 : fm->modes; | 565 | extra_prg = (extra_prg) ? 0 : fm->modes; |
571 | 566 | ||
572 | snd_seq_instr_free_use(opl3->ilist, kinstr); | ||
573 | |||
574 | /* do the bookkeeping */ | 567 | /* do the bookkeeping */ |
575 | vp->time = opl3->use_time++; | 568 | vp->time = opl3->use_time++; |
576 | vp->note = key; | 569 | vp->note = key; |
@@ -601,12 +594,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | |||
601 | /* allocate extra program if specified in patch library */ | 594 | /* allocate extra program if specified in patch library */ |
602 | if (extra_prg) { | 595 | if (extra_prg) { |
603 | if (extra_prg > 128) { | 596 | if (extra_prg > 128) { |
604 | wanted.bank = 128; | 597 | bank = 128; |
605 | /* percussions start at 35 */ | 598 | /* percussions start at 35 */ |
606 | wanted.prg = extra_prg - 128 + 35 - 1; | 599 | prg = extra_prg - 128 + 35 - 1; |
607 | } else { | 600 | } else { |
608 | wanted.bank = 0; | 601 | bank = 0; |
609 | wanted.prg = extra_prg - 1; | 602 | prg = extra_prg - 1; |
610 | } | 603 | } |
611 | #ifdef DEBUG_MIDI | 604 | #ifdef DEBUG_MIDI |
612 | snd_printk(" *** allocating extra program\n"); | 605 | snd_printk(" *** allocating extra program\n"); |
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 5fd3a4c95626..239347f26154 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c | |||
@@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg) | |||
195 | 195 | ||
196 | /* load patch */ | 196 | /* load patch */ |
197 | 197 | ||
198 | /* offsets for SBI params */ | ||
199 | #define AM_VIB 0 | ||
200 | #define KSL_LEVEL 2 | ||
201 | #define ATTACK_DECAY 4 | ||
202 | #define SUSTAIN_RELEASE 6 | ||
203 | #define WAVE_SELECT 8 | ||
204 | |||
205 | /* offset for SBI instrument */ | ||
206 | #define CONNECTION 10 | ||
207 | #define OFFSET_4OP 11 | ||
208 | |||
209 | /* from sound_config.h */ | 198 | /* from sound_config.h */ |
210 | #define SBFM_MAXINSTR 256 | 199 | #define SBFM_MAXINSTR 256 |
211 | 200 | ||
@@ -213,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, | |||
213 | const char __user *buf, int offs, int count) | 202 | const char __user *buf, int offs, int count) |
214 | { | 203 | { |
215 | struct snd_opl3 *opl3; | 204 | struct snd_opl3 *opl3; |
216 | int err = -EINVAL; | 205 | struct sbi_instrument sbi; |
206 | char name[32]; | ||
207 | int err, type; | ||
217 | 208 | ||
218 | snd_assert(arg != NULL, return -ENXIO); | 209 | snd_assert(arg != NULL, return -ENXIO); |
219 | opl3 = arg->private_data; | 210 | opl3 = arg->private_data; |
220 | 211 | ||
221 | if ((format == FM_PATCH) || (format == OPL3_PATCH)) { | 212 | if (format == FM_PATCH) |
222 | struct sbi_instrument sbi; | 213 | type = FM_PATCH_OPL2; |
214 | else if (format == OPL3_PATCH) | ||
215 | type = FM_PATCH_OPL3; | ||
216 | else | ||
217 | return -EINVAL; | ||
223 | 218 | ||
224 | size_t size; | 219 | if (count < (int)sizeof(sbi)) { |
225 | struct snd_seq_instr_header *put; | 220 | snd_printk("FM Error: Patch record too short\n"); |
226 | struct snd_seq_instr_data *data; | 221 | return -EINVAL; |
227 | struct fm_xinstrument *xinstr; | 222 | } |
223 | if (copy_from_user(&sbi, buf, sizeof(sbi))) | ||
224 | return -EFAULT; | ||
228 | 225 | ||
229 | struct snd_seq_event ev; | 226 | if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { |
230 | int i; | 227 | snd_printk("FM Error: Invalid instrument number %d\n", |
228 | sbi.channel); | ||
229 | return -EINVAL; | ||
230 | } | ||
231 | 231 | ||
232 | mm_segment_t fs; | 232 | memset(name, 0, sizeof(name)); |
233 | sprintf(name, "Chan%d", sbi.channel); | ||
233 | 234 | ||
234 | if (count < (int)sizeof(sbi)) { | 235 | err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL, |
235 | snd_printk("FM Error: Patch record too short\n"); | 236 | sbi.operators); |
236 | return -EINVAL; | 237 | if (err < 0) |
237 | } | 238 | return err; |
238 | if (copy_from_user(&sbi, buf, sizeof(sbi))) | ||
239 | return -EFAULT; | ||
240 | 239 | ||
241 | if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { | 240 | return sizeof(sbi); |
242 | snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | |||
246 | size = sizeof(*put) + sizeof(struct fm_xinstrument); | ||
247 | put = kzalloc(size, GFP_KERNEL); | ||
248 | if (put == NULL) | ||
249 | return -ENOMEM; | ||
250 | /* build header */ | ||
251 | data = &put->data; | ||
252 | data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; | ||
253 | strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); | ||
254 | /* build data section */ | ||
255 | xinstr = (struct fm_xinstrument *)(data + 1); | ||
256 | xinstr->stype = FM_STRU_INSTR; | ||
257 | |||
258 | for (i = 0; i < 2; i++) { | ||
259 | xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; | ||
260 | xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; | ||
261 | xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; | ||
262 | xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; | ||
263 | xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; | ||
264 | } | ||
265 | xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; | ||
266 | |||
267 | if (format == OPL3_PATCH) { | ||
268 | xinstr->type = FM_PATCH_OPL3; | ||
269 | for (i = 0; i < 2; i++) { | ||
270 | xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; | ||
271 | xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; | ||
272 | xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; | ||
273 | xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; | ||
274 | xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; | ||
275 | } | ||
276 | xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; | ||
277 | } else { | ||
278 | xinstr->type = FM_PATCH_OPL2; | ||
279 | } | ||
280 | |||
281 | put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; | ||
282 | put->id.instr.bank = 127; | ||
283 | put->id.instr.prg = sbi.channel; | ||
284 | put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; | ||
285 | |||
286 | memset (&ev, 0, sizeof(ev)); | ||
287 | ev.source.client = SNDRV_SEQ_CLIENT_OSS; | ||
288 | ev.dest = arg->addr; | ||
289 | |||
290 | ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; | ||
291 | ev.queue = SNDRV_SEQ_QUEUE_DIRECT; | ||
292 | |||
293 | fs = snd_enter_user(); | ||
294 | __again: | ||
295 | ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; | ||
296 | ev.data.ext.len = size; | ||
297 | ev.data.ext.ptr = put; | ||
298 | |||
299 | err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, | ||
300 | opl3->seq_client, 0, 0); | ||
301 | if (err == -EBUSY) { | ||
302 | struct snd_seq_instr_header remove; | ||
303 | |||
304 | memset (&remove, 0, sizeof(remove)); | ||
305 | remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; | ||
306 | remove.id.instr = put->id.instr; | ||
307 | |||
308 | /* remove instrument */ | ||
309 | ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; | ||
310 | ev.data.ext.len = sizeof(remove); | ||
311 | ev.data.ext.ptr = &remove; | ||
312 | |||
313 | snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, | ||
314 | opl3->seq_client, 0, 0); | ||
315 | goto __again; | ||
316 | } | ||
317 | snd_leave_user(fs); | ||
318 | |||
319 | kfree(put); | ||
320 | } | ||
321 | return err; | ||
322 | } | 241 | } |
323 | 242 | ||
324 | /* ioctl */ | 243 | /* ioctl */ |
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index 96762c9d4855..ff6da16b9178 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c | |||
@@ -152,15 +152,7 @@ static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct, | |||
152 | { | 152 | { |
153 | struct snd_opl3 *opl3 = private_data; | 153 | struct snd_opl3 *opl3 = private_data; |
154 | 154 | ||
155 | if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && | 155 | snd_midi_process_event(&opl3_ops, ev, opl3->chset); |
156 | ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { | ||
157 | if (direct) { | ||
158 | snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, | ||
159 | opl3->seq_client, atomic, hop); | ||
160 | } | ||
161 | } else { | ||
162 | snd_midi_process_event(&opl3_ops, ev, opl3->chset); | ||
163 | } | ||
164 | return 0; | 156 | return 0; |
165 | } | 157 | } |
166 | 158 | ||
@@ -249,16 +241,6 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) | |||
249 | return err; | 241 | return err; |
250 | } | 242 | } |
251 | 243 | ||
252 | /* initialize instrument list */ | ||
253 | opl3->ilist = snd_seq_instr_list_new(); | ||
254 | if (opl3->ilist == NULL) { | ||
255 | snd_seq_delete_kernel_client(client); | ||
256 | opl3->seq_client = -1; | ||
257 | return -ENOMEM; | ||
258 | } | ||
259 | opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; | ||
260 | snd_seq_fm_init(&opl3->fm_ops, NULL); | ||
261 | |||
262 | /* setup system timer */ | 244 | /* setup system timer */ |
263 | init_timer(&opl3->tlist); | 245 | init_timer(&opl3->tlist); |
264 | opl3->tlist.function = snd_opl3_timer_func; | 246 | opl3->tlist.function = snd_opl3_timer_func; |
@@ -287,8 +269,6 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) | |||
287 | snd_seq_delete_kernel_client(opl3->seq_client); | 269 | snd_seq_delete_kernel_client(opl3->seq_client); |
288 | opl3->seq_client = -1; | 270 | opl3->seq_client = -1; |
289 | } | 271 | } |
290 | if (opl3->ilist) | ||
291 | snd_seq_instr_list_free(&opl3->ilist); | ||
292 | return 0; | 272 | return 0; |
293 | } | 273 | } |
294 | 274 | ||
diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index a4b3543a7118..d55eefce44c1 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c | |||
@@ -165,6 +165,10 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, | |||
165 | #endif | 165 | #endif |
166 | return snd_opl3_set_connection(opl3, (int) arg); | 166 | return snd_opl3_set_connection(opl3, (int) arg); |
167 | 167 | ||
168 | case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES: | ||
169 | snd_opl3_clear_patches(opl3); | ||
170 | return 0; | ||
171 | |||
168 | #ifdef CONFIG_SND_DEBUG | 172 | #ifdef CONFIG_SND_DEBUG |
169 | default: | 173 | default: |
170 | snd_printk("unknown IOCTL: 0x%x\n", cmd); | 174 | snd_printk("unknown IOCTL: 0x%x\n", cmd); |
@@ -188,6 +192,170 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file) | |||
188 | return 0; | 192 | return 0; |
189 | } | 193 | } |
190 | 194 | ||
195 | /* | ||
196 | * write the device - load patches | ||
197 | */ | ||
198 | long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, | ||
199 | loff_t *offset) | ||
200 | { | ||
201 | struct snd_opl3 *opl3 = hw->private_data; | ||
202 | long result = 0; | ||
203 | int err = 0; | ||
204 | struct sbi_patch inst; | ||
205 | |||
206 | while (count >= sizeof(inst)) { | ||
207 | unsigned char type; | ||
208 | if (copy_from_user(&inst, buf, sizeof(inst))) | ||
209 | return -EFAULT; | ||
210 | if (!memcmp(inst.key, FM_KEY_SBI, 4) || | ||
211 | !memcmp(inst.key, FM_KEY_2OP, 4)) | ||
212 | type = FM_PATCH_OPL2; | ||
213 | else if (!memcmp(inst.key, FM_KEY_4OP, 4)) | ||
214 | type = FM_PATCH_OPL3; | ||
215 | else /* invalid type */ | ||
216 | break; | ||
217 | err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type, | ||
218 | inst.name, inst.extension, | ||
219 | inst.data); | ||
220 | if (err < 0) | ||
221 | break; | ||
222 | result += sizeof(inst); | ||
223 | count -= sizeof(inst); | ||
224 | } | ||
225 | return result > 0 ? result : err; | ||
226 | } | ||
227 | |||
228 | |||
229 | /* | ||
230 | * Patch management | ||
231 | */ | ||
232 | |||
233 | /* offsets for SBI params */ | ||
234 | #define AM_VIB 0 | ||
235 | #define KSL_LEVEL 2 | ||
236 | #define ATTACK_DECAY 4 | ||
237 | #define SUSTAIN_RELEASE 6 | ||
238 | #define WAVE_SELECT 8 | ||
239 | |||
240 | /* offset for SBI instrument */ | ||
241 | #define CONNECTION 10 | ||
242 | #define OFFSET_4OP 11 | ||
243 | |||
244 | /* | ||
245 | * load a patch, obviously. | ||
246 | * | ||
247 | * loaded on the given program and bank numbers with the given type | ||
248 | * (FM_PATCH_OPLx). | ||
249 | * data is the pointer of SBI record _without_ header (key and name). | ||
250 | * name is the name string of the patch. | ||
251 | * ext is the extension data of 7 bytes long (stored in name of SBI | ||
252 | * data up to offset 25), or NULL to skip. | ||
253 | * return 0 if successful or a negative error code. | ||
254 | */ | ||
255 | int snd_opl3_load_patch(struct snd_opl3 *opl3, | ||
256 | int prog, int bank, int type, | ||
257 | const char *name, | ||
258 | const unsigned char *ext, | ||
259 | const unsigned char *data) | ||
260 | { | ||
261 | struct fm_patch *patch; | ||
262 | int i; | ||
263 | |||
264 | patch = snd_opl3_find_patch(opl3, prog, bank, 1); | ||
265 | if (!patch) | ||
266 | return -ENOMEM; | ||
267 | |||
268 | patch->type = type; | ||
269 | |||
270 | for (i = 0; i < 2; i++) { | ||
271 | patch->inst.op[i].am_vib = data[AM_VIB + i]; | ||
272 | patch->inst.op[i].ksl_level = data[KSL_LEVEL + i]; | ||
273 | patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i]; | ||
274 | patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i]; | ||
275 | patch->inst.op[i].wave_select = data[WAVE_SELECT + i]; | ||
276 | } | ||
277 | patch->inst.feedback_connection[0] = data[CONNECTION]; | ||
278 | |||
279 | if (type == FM_PATCH_OPL3) { | ||
280 | for (i = 0; i < 2; i++) { | ||
281 | patch->inst.op[i+2].am_vib = | ||
282 | data[OFFSET_4OP + AM_VIB + i]; | ||
283 | patch->inst.op[i+2].ksl_level = | ||
284 | data[OFFSET_4OP + KSL_LEVEL + i]; | ||
285 | patch->inst.op[i+2].attack_decay = | ||
286 | data[OFFSET_4OP + ATTACK_DECAY + i]; | ||
287 | patch->inst.op[i+2].sustain_release = | ||
288 | data[OFFSET_4OP + SUSTAIN_RELEASE + i]; | ||
289 | patch->inst.op[i+2].wave_select = | ||
290 | data[OFFSET_4OP + WAVE_SELECT + i]; | ||
291 | } | ||
292 | patch->inst.feedback_connection[1] = | ||
293 | data[OFFSET_4OP + CONNECTION]; | ||
294 | } | ||
295 | |||
296 | if (ext) { | ||
297 | patch->inst.echo_delay = ext[0]; | ||
298 | patch->inst.echo_atten = ext[1]; | ||
299 | patch->inst.chorus_spread = ext[2]; | ||
300 | patch->inst.trnsps = ext[3]; | ||
301 | patch->inst.fix_dur = ext[4]; | ||
302 | patch->inst.modes = ext[5]; | ||
303 | patch->inst.fix_key = ext[6]; | ||
304 | } | ||
305 | |||
306 | if (name) | ||
307 | strlcpy(patch->name, name, sizeof(patch->name)); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | EXPORT_SYMBOL(snd_opl3_load_patch); | ||
312 | |||
313 | /* | ||
314 | * find a patch with the given program and bank numbers, returns its pointer | ||
315 | * if no matching patch is found and create_patch is set, it creates a | ||
316 | * new patch object. | ||
317 | */ | ||
318 | struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, | ||
319 | int create_patch) | ||
320 | { | ||
321 | /* pretty dumb hash key */ | ||
322 | unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE; | ||
323 | struct fm_patch *patch; | ||
324 | |||
325 | for (patch = opl3->patch_table[key]; patch; patch = patch->next) { | ||
326 | if (patch->prog == prog && patch->bank == bank) | ||
327 | return patch; | ||
328 | } | ||
329 | if (!create_patch) | ||
330 | return NULL; | ||
331 | |||
332 | patch = kzalloc(sizeof(*patch), GFP_KERNEL); | ||
333 | if (!patch) | ||
334 | return NULL; | ||
335 | patch->prog = prog; | ||
336 | patch->bank = bank; | ||
337 | patch->next = opl3->patch_table[key]; | ||
338 | opl3->patch_table[key] = patch; | ||
339 | return patch; | ||
340 | } | ||
341 | EXPORT_SYMBOL(snd_opl3_find_patch); | ||
342 | |||
343 | /* | ||
344 | * Clear all patches of the given OPL3 instance | ||
345 | */ | ||
346 | void snd_opl3_clear_patches(struct snd_opl3 *opl3) | ||
347 | { | ||
348 | int i; | ||
349 | for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) { | ||
350 | struct fm_patch *patch, *next; | ||
351 | for (patch = opl3->patch_table[i]; patch; patch = next) { | ||
352 | next = patch->next; | ||
353 | kfree(patch); | ||
354 | } | ||
355 | } | ||
356 | memset(opl3->patch_table, 0, sizeof(opl3->patch_table)); | ||
357 | } | ||
358 | |||
191 | /* ------------------------------ */ | 359 | /* ------------------------------ */ |
192 | 360 | ||
193 | void snd_opl3_reset(struct snd_opl3 * opl3) | 361 | void snd_opl3_reset(struct snd_opl3 * opl3) |