1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Interface for OSS sequencer emulation 4 * 5 * Copyright (C) 1999 Takashi Iwai <tiwai@suse.de> 6 * 7 * Changes 8 * 19990227 Steve Ratcliffe Made separate file and merged in latest 9 * midi emulation. 10 */ 11 12 13 #include <linux/export.h> 14 #include <linux/uaccess.h> 15 #include <sound/core.h> 16 #include "emux_voice.h" 17 #include <sound/asoundef.h> 18 19 static int snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure); 20 static int snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg); 21 static int snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, 22 unsigned long ioarg); 23 static int snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, 24 const char __user *buf, int offs, int count); 25 static int snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg); 26 static int snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, 27 void *private, int atomic, int hop); 28 static void reset_port_mode(struct snd_emux_port *port, int midi_mode); 29 static void emuspec_control(struct snd_emux *emu, struct snd_emux_port *port, 30 int cmd, unsigned char *event, int atomic, int hop); 31 static void gusspec_control(struct snd_emux *emu, struct snd_emux_port *port, 32 int cmd, unsigned char *event, int atomic, int hop); 33 static void fake_event(struct snd_emux *emu, struct snd_emux_port *port, 34 int ch, int param, int val, int atomic, int hop); 35 36 /* operators */ 37 static const struct snd_seq_oss_callback oss_callback = { 38 .owner = THIS_MODULE, 39 .open = snd_emux_open_seq_oss, 40 .close = snd_emux_close_seq_oss, 41 .ioctl = snd_emux_ioctl_seq_oss, 42 .load_patch = snd_emux_load_patch_seq_oss, 43 .reset = snd_emux_reset_seq_oss, 44 }; 45 46 47 /* 48 * register OSS synth 49 */ 50 51 void 52 snd_emux_init_seq_oss(struct snd_emux *emu) 53 { 54 struct snd_seq_oss_reg *arg; 55 struct snd_seq_device *dev; 56 57 /* using device#1 here for avoiding conflicts with OPL3 */ 58 if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS, 59 sizeof(struct snd_seq_oss_reg), &dev) < 0) 60 return; 61 62 emu->oss_synth = dev; 63 strcpy(dev->name, emu->name); 64 arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); 65 arg->type = SYNTH_TYPE_SAMPLE; 66 arg->subtype = SAMPLE_TYPE_AWE32; 67 arg->nvoices = emu->max_voices; 68 arg->oper = oss_callback; 69 arg->private_data = emu; 70 71 /* register to OSS synth table */ 72 snd_device_register(emu->card, dev); 73 } 74 75 76 /* 77 * unregister 78 */ 79 void 80 snd_emux_detach_seq_oss(struct snd_emux *emu) 81 { 82 if (emu->oss_synth) { 83 snd_device_free(emu->card, emu->oss_synth); 84 emu->oss_synth = NULL; 85 } 86 } 87 88 89 /* use port number as a unique soundfont client number */ 90 #define SF_CLIENT_NO(p) ((p) + 0x1000) 91 92 /* 93 * open port for OSS sequencer 94 */ 95 static int 96 snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure) 97 { 98 struct snd_emux *emu; 99 struct snd_emux_port *p; 100 struct snd_seq_port_callback callback; 101 char tmpname[64]; 102 103 emu = closure; 104 if (snd_BUG_ON(!arg || !emu)) 105 return -ENXIO; 106 107 if (!snd_emux_inc_count(emu)) 108 return -EFAULT; 109 110 memset(&callback, 0, sizeof(callback)); 111 callback.owner = THIS_MODULE; 112 callback.event_input = snd_emux_event_oss_input; 113 114 sprintf(tmpname, "%s OSS Port", emu->name); 115 p = snd_emux_create_port(emu, tmpname, 32, 116 1, &callback); 117 if (p == NULL) { 118 snd_printk(KERN_ERR "can't create port\n"); 119 snd_emux_dec_count(emu); 120 return -ENOMEM; 121 } 122 123 /* fill the argument data */ 124 arg->private_data = p; 125 arg->addr.client = p->chset.client; 126 arg->addr.port = p->chset.port; 127 p->oss_arg = arg; 128 129 reset_port_mode(p, arg->seq_mode); 130 131 snd_emux_reset_port(p); 132 return 0; 133 } 134 135 136 #define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25)) 137 138 /* 139 * reset port mode 140 */ 141 static void 142 reset_port_mode(struct snd_emux_port *port, int midi_mode) 143 { 144 if (midi_mode) { 145 port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI; 146 port->drum_flags = DEFAULT_DRUM_FLAGS; 147 port->volume_atten = 0; 148 port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS; 149 } else { 150 port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH; 151 port->drum_flags = 0; 152 port->volume_atten = 32; 153 port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; 154 } 155 } 156 157 158 /* 159 * close port 160 */ 161 static int 162 snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg) 163 { 164 struct snd_emux *emu; 165 struct snd_emux_port *p; 166 167 if (snd_BUG_ON(!arg)) 168 return -ENXIO; 169 p = arg->private_data; 170 if (snd_BUG_ON(!p)) 171 return -ENXIO; 172 173 emu = p->emu; 174 if (snd_BUG_ON(!emu)) 175 return -ENXIO; 176 177 snd_emux_sounds_off_all(p); 178 snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); 179 snd_seq_event_port_detach(p->chset.client, p->chset.port); 180 snd_emux_dec_count(emu); 181 182 return 0; 183 } 184 185 186 /* 187 * load patch 188 */ 189 static int 190 snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, 191 const char __user *buf, int offs, int count) 192 { 193 struct snd_emux *emu; 194 struct snd_emux_port *p; 195 int rc; 196 197 if (snd_BUG_ON(!arg)) 198 return -ENXIO; 199 p = arg->private_data; 200 if (snd_BUG_ON(!p)) 201 return -ENXIO; 202 203 emu = p->emu; 204 if (snd_BUG_ON(!emu)) 205 return -ENXIO; 206 207 if (format == GUS_PATCH) 208 rc = snd_soundfont_load_guspatch(emu->sflist, buf, count); 209 else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { 210 struct soundfont_patch_info patch; 211 if (count < (int)sizeof(patch)) 212 return -EINVAL; 213 if (copy_from_user(&patch, buf, sizeof(patch))) 214 return -EFAULT; 215 if (patch.type >= SNDRV_SFNT_LOAD_INFO && 216 patch.type <= SNDRV_SFNT_PROBE_DATA) 217 rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port)); 218 else { 219 if (emu->ops.load_fx) 220 rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count); 221 else 222 rc = -EINVAL; 223 } 224 } else 225 rc = 0; 226 return rc; 227 } 228 229 230 /* 231 * ioctl 232 */ 233 static int 234 snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg) 235 { 236 struct snd_emux_port *p; 237 struct snd_emux *emu; 238 239 if (snd_BUG_ON(!arg)) 240 return -ENXIO; 241 p = arg->private_data; 242 if (snd_BUG_ON(!p)) 243 return -ENXIO; 244 245 emu = p->emu; 246 if (snd_BUG_ON(!emu)) 247 return -ENXIO; 248 249 switch (cmd) { 250 case SNDCTL_SEQ_RESETSAMPLES: 251 snd_soundfont_remove_samples(emu->sflist); 252 return 0; 253 254 case SNDCTL_SYNTH_MEMAVL: 255 if (emu->memhdr) 256 return snd_util_mem_avail(emu->memhdr); 257 return 0; 258 } 259 260 return 0; 261 } 262 263 264 /* 265 * reset device 266 */ 267 static int 268 snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg) 269 { 270 struct snd_emux_port *p; 271 272 if (snd_BUG_ON(!arg)) 273 return -ENXIO; 274 p = arg->private_data; 275 if (snd_BUG_ON(!p)) 276 return -ENXIO; 277 snd_emux_reset_port(p); 278 return 0; 279 } 280 281 282 /* 283 * receive raw events: only SEQ_PRIVATE is accepted. 284 */ 285 static int 286 snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_data, 287 int atomic, int hop) 288 { 289 struct snd_emux *emu; 290 struct snd_emux_port *p; 291 unsigned char cmd, *data; 292 293 p = private_data; 294 if (snd_BUG_ON(!p)) 295 return -EINVAL; 296 emu = p->emu; 297 if (snd_BUG_ON(!emu)) 298 return -EINVAL; 299 if (ev->type != SNDRV_SEQ_EVENT_OSS) 300 return snd_emux_event_input(ev, direct, private_data, atomic, hop); 301 302 data = ev->data.raw8.d; 303 /* only SEQ_PRIVATE is accepted */ 304 if (data[0] != 0xfe) 305 return 0; 306 cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK; 307 if (data[2] & _EMUX_OSS_MODE_FLAG) 308 emuspec_control(emu, p, cmd, data, atomic, hop); 309 else 310 gusspec_control(emu, p, cmd, data, atomic, hop); 311 return 0; 312 } 313 314 315 /* 316 * OSS/AWE driver specific h/w controls 317 */ 318 static void 319 emuspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd, 320 unsigned char *event, int atomic, int hop) 321 { 322 int voice; 323 unsigned short p1; 324 short p2; 325 int i; 326 struct snd_midi_channel *chan; 327 328 voice = event[3]; 329 if (voice < 0 || voice >= port->chset.max_channels) 330 chan = NULL; 331 else 332 chan = &port->chset.channels[voice]; 333 334 p1 = *(unsigned short *) &event[4]; 335 p2 = *(short *) &event[6]; 336 337 switch (cmd) { 338 #if 0 /* don't do this atomically */ 339 case _EMUX_OSS_REMOVE_LAST_SAMPLES: 340 snd_soundfont_remove_unlocked(emu->sflist); 341 break; 342 #endif 343 case _EMUX_OSS_SEND_EFFECT: 344 if (chan) 345 snd_emux_send_effect_oss(port, chan, p1, p2); 346 break; 347 348 case _EMUX_OSS_TERMINATE_ALL: 349 snd_emux_terminate_all(emu); 350 break; 351 352 case _EMUX_OSS_TERMINATE_CHANNEL: 353 /*snd_emux_mute_channel(emu, chan);*/ 354 break; 355 case _EMUX_OSS_RESET_CHANNEL: 356 /*snd_emux_channel_init(chset, chan);*/ 357 break; 358 359 case _EMUX_OSS_RELEASE_ALL: 360 fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop); 361 break; 362 case _EMUX_OSS_NOTEOFF_ALL: 363 fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop); 364 break; 365 366 case _EMUX_OSS_INITIAL_VOLUME: 367 if (p2) { 368 port->volume_atten = (short)p1; 369 snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); 370 } 371 break; 372 373 case _EMUX_OSS_CHN_PRESSURE: 374 if (chan) { 375 chan->midi_pressure = p1; 376 snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2); 377 } 378 break; 379 380 case _EMUX_OSS_CHANNEL_MODE: 381 reset_port_mode(port, p1); 382 snd_emux_reset_port(port); 383 break; 384 385 case _EMUX_OSS_DRUM_CHANNELS: 386 port->drum_flags = *(unsigned int*)&event[4]; 387 for (i = 0; i < port->chset.max_channels; i++) { 388 chan = &port->chset.channels[i]; 389 chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; 390 } 391 break; 392 393 case _EMUX_OSS_MISC_MODE: 394 if (p1 < EMUX_MD_END) 395 port->ctrls[p1] = p2; 396 break; 397 case _EMUX_OSS_DEBUG_MODE: 398 break; 399 400 default: 401 if (emu->ops.oss_ioctl) 402 emu->ops.oss_ioctl(emu, cmd, p1, p2); 403 break; 404 } 405 } 406 407 /* 408 * GUS specific h/w controls 409 */ 410 411 #include <linux/ultrasound.h> 412 413 static void 414 gusspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd, 415 unsigned char *event, int atomic, int hop) 416 { 417 int voice; 418 unsigned short p1; 419 int plong; 420 struct snd_midi_channel *chan; 421 422 if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH) 423 return; 424 if (cmd == _GUS_NUMVOICES) 425 return; 426 voice = event[3]; 427 if (voice < 0 || voice >= port->chset.max_channels) 428 return; 429 430 chan = &port->chset.channels[voice]; 431 432 p1 = *(unsigned short *) &event[4]; 433 plong = *(int*) &event[4]; 434 435 switch (cmd) { 436 case _GUS_VOICESAMPLE: 437 chan->midi_program = p1; 438 return; 439 440 case _GUS_VOICEBALA: 441 /* 0 to 15 --> 0 to 127 */ 442 chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3; 443 snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); 444 return; 445 446 case _GUS_VOICEVOL: 447 case _GUS_VOICEVOL2: 448 /* not supported yet */ 449 return; 450 451 case _GUS_RAMPRANGE: 452 case _GUS_RAMPRATE: 453 case _GUS_RAMPMODE: 454 case _GUS_RAMPON: 455 case _GUS_RAMPOFF: 456 /* volume ramping not supported */ 457 return; 458 459 case _GUS_VOLUME_SCALE: 460 return; 461 462 case _GUS_VOICE_POS: 463 #ifdef SNDRV_EMUX_USE_RAW_EFFECT 464 snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START, 465 (short)(plong & 0x7fff), 466 EMUX_FX_FLAG_SET); 467 snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START, 468 (plong >> 15) & 0xffff, 469 EMUX_FX_FLAG_SET); 470 #endif 471 return; 472 } 473 } 474 475 476 /* 477 * send an event to midi emulation 478 */ 479 static void 480 fake_event(struct snd_emux *emu, struct snd_emux_port *port, int ch, int param, int val, int atomic, int hop) 481 { 482 struct snd_seq_event ev; 483 memset(&ev, 0, sizeof(ev)); 484 ev.type = SNDRV_SEQ_EVENT_CONTROLLER; 485 ev.data.control.channel = ch; 486 ev.data.control.param = param; 487 ev.data.control.value = val; 488 snd_emux_event_input(&ev, 0, port, atomic, hop); 489 } 490
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.