FILE
seq_oss_synth.c
File preview with syntax highlighting.
linux-6.19.14/sound/core/seq/oss/seq_oss_synth.c
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * OSS compatible sequencer driver 4 * 5 * synth device handlers 6 * 7 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> 8 */ 9 10#include "seq_oss_synth.h" 11#include "seq_oss_midi.h" 12#include "../seq_lock.h" 13#include <linux/init.h> 14#include <linux/module.h> 15#include <linux/slab.h> 16#include <linux/nospec.h> 17 18/* 19 * constants 20 */ 21#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30 22#define MAX_SYSEX_BUFLEN 128 23 24 25/* 26 * definition of synth info records 27 */ 28 29/* synth info */ 30struct seq_oss_synth { 31 int seq_device; 32 33 /* for synth_info */ 34 int synth_type; 35 int synth_subtype; 36 int nr_voices; 37 38 char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME]; 39 struct snd_seq_oss_callback oper; 40 41 int opened; 42 43 void *private_data; 44 snd_use_lock_t use_lock; 45}; 46 47DEFINE_FREE(seq_oss_synth, struct seq_oss_synth *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock)) 48 49/* 50 * device table 51 */ 52static int max_synth_devs; 53static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; 54static struct seq_oss_synth midi_synth_dev = { 55 .seq_device = -1, 56 .synth_type = SYNTH_TYPE_MIDI, 57 .synth_subtype = 0, 58 .nr_voices = 16, 59 .name = "MIDI", 60}; 61 62static DEFINE_SPINLOCK(register_lock); 63 64/* 65 * prototypes 66 */ 67static struct seq_oss_synth *get_synthdev(struct seq_oss_devinfo *dp, int dev); 68static void reset_channels(struct seq_oss_synthinfo *info); 69 70/* 71 * global initialization 72 */ 73void __init 74snd_seq_oss_synth_init(void) 75{ 76 snd_use_lock_init(&midi_synth_dev.use_lock); 77} 78 79/* 80 * registration of the synth device 81 */ 82int 83snd_seq_oss_synth_probe(struct device *_dev) 84{ 85 struct snd_seq_device *dev = to_seq_dev(_dev); 86 int i; 87 struct seq_oss_synth *rec; 88 struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); 89 90 rec = kzalloc(sizeof(*rec), GFP_KERNEL); 91 if (!rec) 92 return -ENOMEM; 93 rec->seq_device = -1; 94 rec->synth_type = reg->type; 95 rec->synth_subtype = reg->subtype; 96 rec->nr_voices = reg->nvoices; 97 rec->oper = reg->oper; 98 rec->private_data = reg->private_data; 99 rec->opened = 0; 100 snd_use_lock_init(&rec->use_lock); 101 102 /* copy and truncate the name of synth device */ 103 strscpy(rec->name, dev->name, sizeof(rec->name)); 104 105 /* registration */ 106 scoped_guard(spinlock_irqsave, ®ister_lock) { 107 for (i = 0; i < max_synth_devs; i++) { 108 if (synth_devs[i] == NULL) 109 break; 110 } 111 if (i >= max_synth_devs) { 112 if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { 113 pr_err("ALSA: seq_oss: no more synth slot\n"); 114 kfree(rec); 115 return -ENOMEM; 116 } 117 max_synth_devs++; 118 } 119 rec->seq_device = i; 120 synth_devs[i] = rec; 121 } 122 dev->driver_data = rec; 123#ifdef SNDRV_OSS_INFO_DEV_SYNTH 124 if (i < SNDRV_CARDS) 125 snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name); 126#endif 127 return 0; 128} 129 130 131int 132snd_seq_oss_synth_remove(struct device *_dev) 133{ 134 struct snd_seq_device *dev = to_seq_dev(_dev); 135 int index; 136 struct seq_oss_synth *rec = dev->driver_data; 137 138 scoped_guard(spinlock_irqsave, ®ister_lock) { 139 for (index = 0; index < max_synth_devs; index++) { 140 if (synth_devs[index] == rec) 141 break; 142 } 143 if (index >= max_synth_devs) { 144 pr_err("ALSA: seq_oss: can't unregister synth\n"); 145 return -EINVAL; 146 } 147 synth_devs[index] = NULL; 148 if (index == max_synth_devs - 1) { 149 for (index--; index >= 0; index--) { 150 if (synth_devs[index]) 151 break; 152 } 153 max_synth_devs = index + 1; 154 } 155 } 156#ifdef SNDRV_OSS_INFO_DEV_SYNTH 157 if (rec->seq_device < SNDRV_CARDS) 158 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device); 159#endif 160 161 snd_use_lock_sync(&rec->use_lock); 162 kfree(rec); 163 164 return 0; 165} 166 167 168/* 169 */ 170static struct seq_oss_synth * 171get_sdev(int dev) 172{ 173 struct seq_oss_synth *rec; 174 175 guard(spinlock_irqsave)(®ister_lock); 176 rec = synth_devs[dev]; 177 if (rec) 178 snd_use_lock_use(&rec->use_lock); 179 return rec; 180} 181 182 183/* 184 * set up synth tables 185 */ 186 187void 188snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) 189{ 190 int i; 191 struct seq_oss_synthinfo *info; 192 193 dp->max_synthdev = max_synth_devs; 194 dp->synth_opened = 0; 195 memset(dp->synths, 0, sizeof(dp->synths)); 196 for (i = 0; i < dp->max_synthdev; i++) { 197 struct seq_oss_synth *rec __free(seq_oss_synth) = get_sdev(i); 198 199 if (rec == NULL) 200 continue; 201 if (rec->oper.open == NULL || rec->oper.close == NULL) 202 continue; 203 info = &dp->synths[i]; 204 info->arg.app_index = dp->port; 205 info->arg.file_mode = dp->file_mode; 206 info->arg.seq_mode = dp->seq_mode; 207 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) 208 info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; 209 else 210 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; 211 info->opened = 0; 212 if (!try_module_get(rec->oper.owner)) 213 continue; 214 if (rec->oper.open(&info->arg, rec->private_data) < 0) { 215 module_put(rec->oper.owner); 216 continue; 217 } 218 info->nr_voices = rec->nr_voices; 219 if (info->nr_voices > 0) { 220 info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); 221 if (!info->ch) { 222 rec->oper.close(&info->arg); 223 module_put(rec->oper.owner); 224 continue; 225 } 226 reset_channels(info); 227 } 228 info->opened++; 229 rec->opened++; 230 dp->synth_opened++; 231 } 232} 233 234 235/* 236 * set up synth tables for MIDI emulation - /dev/music mode only 237 */ 238 239void 240snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp) 241{ 242 int i; 243 244 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) 245 return; 246 247 for (i = 0; i < dp->max_mididev; i++) { 248 struct seq_oss_synthinfo *info; 249 info = &dp->synths[dp->max_synthdev]; 250 if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0) 251 continue; 252 info->arg.app_index = dp->port; 253 info->arg.file_mode = dp->file_mode; 254 info->arg.seq_mode = dp->seq_mode; 255 info->arg.private_data = info; 256 info->is_midi = 1; 257 info->midi_mapped = i; 258 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; 259 snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr); 260 info->opened = 1; 261 midi_synth_dev.opened++; 262 dp->max_synthdev++; 263 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) 264 break; 265 } 266} 267 268 269/* 270 * clean up synth tables 271 */ 272 273void 274snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) 275{ 276 int i; 277 struct seq_oss_synthinfo *info; 278 279 if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) 280 return; 281 for (i = 0; i < dp->max_synthdev; i++) { 282 info = &dp->synths[i]; 283 if (! info->opened) 284 continue; 285 if (info->is_midi) { 286 if (midi_synth_dev.opened > 0) { 287 snd_seq_oss_midi_close(dp, info->midi_mapped); 288 midi_synth_dev.opened--; 289 } 290 } else { 291 struct seq_oss_synth *rec __free(seq_oss_synth) = 292 get_sdev(i); 293 294 if (rec == NULL) 295 continue; 296 if (rec->opened > 0) { 297 rec->oper.close(&info->arg); 298 module_put(rec->oper.owner); 299 rec->opened = 0; 300 } 301 } 302 kfree(info->ch); 303 info->ch = NULL; 304 } 305 dp->synth_opened = 0; 306 dp->max_synthdev = 0; 307} 308 309static struct seq_oss_synthinfo * 310get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev) 311{ 312 if (dev < 0 || dev >= dp->max_synthdev) 313 return NULL; 314 dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS); 315 return &dp->synths[dev]; 316} 317 318/* 319 * return synth device information pointer 320 */ 321static struct seq_oss_synth * 322get_synthdev(struct seq_oss_devinfo *dp, int dev) 323{ 324 struct seq_oss_synth *rec; 325 struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev); 326 327 if (!info) 328 return NULL; 329 if (!info->opened) 330 return NULL; 331 if (info->is_midi) { 332 rec = &midi_synth_dev; 333 snd_use_lock_use(&rec->use_lock); 334 } else { 335 rec = get_sdev(dev); 336 if (!rec) 337 return NULL; 338 } 339 if (! rec->opened) { 340 snd_use_lock_free(&rec->use_lock); 341 return NULL; 342 } 343 return rec; 344} 345 346 347/* 348 * reset note and velocity on each channel. 349 */ 350static void 351reset_channels(struct seq_oss_synthinfo *info) 352{ 353 int i; 354 if (info->ch == NULL || ! info->nr_voices) 355 return; 356 for (i = 0; i < info->nr_voices; i++) { 357 info->ch[i].note = -1; 358 info->ch[i].vel = 0; 359 } 360} 361 362 363/* 364 * reset synth device: 365 * call reset callback. if no callback is defined, send a heartbeat 366 * event to the corresponding port. 367 */ 368void 369snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) 370{ 371 struct seq_oss_synthinfo *info; 372 373 info = get_synthinfo_nospec(dp, dev); 374 if (!info || !info->opened) 375 return; 376 reset_channels(info); 377 if (info->is_midi) { 378 if (midi_synth_dev.opened <= 0) 379 return; 380 snd_seq_oss_midi_reset(dp, info->midi_mapped); 381 /* reopen the device */ 382 snd_seq_oss_midi_close(dp, dev); 383 if (snd_seq_oss_midi_open(dp, info->midi_mapped, 384 dp->file_mode) < 0) { 385 midi_synth_dev.opened--; 386 info->opened = 0; 387 kfree(info->ch); 388 info->ch = NULL; 389 } 390 return; 391 } 392 393 struct seq_oss_synth *rec __free(seq_oss_synth) = 394 get_sdev(dev); 395 if (rec == NULL) 396 return; 397 if (rec->oper.reset) { 398 rec->oper.reset(&info->arg); 399 } else { 400 struct snd_seq_event ev; 401 memset(&ev, 0, sizeof(ev)); 402 snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client, 403 info->arg.addr.port); 404 ev.type = SNDRV_SEQ_EVENT_RESET; 405 snd_seq_oss_dispatch(dp, &ev, 0, 0); 406 } 407} 408 409 410/* 411 * load a patch record: 412 * call load_patch callback function 413 */ 414int 415snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt, 416 const char __user *buf, int p, int c) 417{ 418 struct seq_oss_synthinfo *info; 419 420 info = get_synthinfo_nospec(dp, dev); 421 if (!info) 422 return -ENXIO; 423 424 if (info->is_midi) 425 return 0; 426 427 struct seq_oss_synth *rec __free(seq_oss_synth) = 428 get_synthdev(dp, dev); 429 if (!rec) 430 return -ENXIO; 431 432 if (rec->oper.load_patch == NULL) 433 return -ENXIO; 434 else 435 return rec->oper.load_patch(&info->arg, fmt, buf, p, c); 436} 437 438/* 439 * check if the device is valid synth device and return the synth info 440 */ 441struct seq_oss_synthinfo * 442snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev) 443{ 444 struct seq_oss_synth *rec __free(seq_oss_synth) = 445 get_synthdev(dp, dev); 446 447 if (rec) 448 return get_synthinfo_nospec(dp, dev); 449 return NULL; 450} 451 452 453/* 454 * receive OSS 6 byte sysex packet: 455 * the event is filled and prepared for sending immediately 456 * (i.e. sysex messages are fragmented) 457 */ 458int 459snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev) 460{ 461 unsigned char *p; 462 int len = 6; 463 464 p = memchr(buf, 0xff, 6); 465 if (p) 466 len = p - buf + 1; 467 468 /* copy the data to event record and send it */ 469 if (snd_seq_oss_synth_addr(dp, dev, ev)) 470 return -EINVAL; 471 ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; 472 ev->data.ext.len = len; 473 ev->data.ext.ptr = buf; 474 return 0; 475} 476 477/* 478 * fill the event source/destination addresses 479 */ 480int 481snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev) 482{ 483 struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev); 484 485 if (!info) 486 return -EINVAL; 487 snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client, 488 info->arg.addr.port); 489 return 0; 490} 491 492 493/* 494 * OSS compatible ioctl 495 */ 496int 497snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr) 498{ 499 struct seq_oss_synthinfo *info; 500 501 info = get_synthinfo_nospec(dp, dev); 502 if (!info || info->is_midi) 503 return -ENXIO; 504 505 struct seq_oss_synth *rec __free(seq_oss_synth) = 506 get_synthdev(dp, dev); 507 if (!rec) 508 return -ENXIO; 509 if (rec->oper.ioctl == NULL) 510 return -ENXIO; 511 else 512 return rec->oper.ioctl(&info->arg, cmd, addr); 513} 514 515 516/* 517 * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME 518 */ 519int 520snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev) 521{ 522 struct seq_oss_synthinfo *info; 523 524 info = snd_seq_oss_synth_info(dp, dev); 525 if (!info || info->is_midi) 526 return -ENXIO; 527 ev->type = SNDRV_SEQ_EVENT_OSS; 528 memcpy(ev->data.raw8.d, data, 8); 529 return snd_seq_oss_synth_addr(dp, dev, ev); 530} 531 532 533/* 534 * create OSS compatible synth_info record 535 */ 536int 537snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf) 538{ 539 struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev); 540 541 if (!info) 542 return -ENXIO; 543 544 if (info->is_midi) { 545 struct midi_info minf; 546 if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf)) 547 return -ENXIO; 548 inf->synth_type = SYNTH_TYPE_MIDI; 549 inf->synth_subtype = 0; 550 inf->nr_voices = 16; 551 inf->device = dev; 552 strscpy(inf->name, minf.name, sizeof(inf->name)); 553 } else { 554 struct seq_oss_synth *rec __free(seq_oss_synth) = 555 get_synthdev(dp, dev); 556 557 if (!rec) 558 return -ENXIO; 559 inf->synth_type = rec->synth_type; 560 inf->synth_subtype = rec->synth_subtype; 561 inf->nr_voices = rec->nr_voices; 562 inf->device = dev; 563 strscpy(inf->name, rec->name, sizeof(inf->name)); 564 } 565 return 0; 566} 567 568 569#ifdef CONFIG_SND_PROC_FS 570/* 571 * proc interface 572 */ 573void 574snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) 575{ 576 int i; 577 578 snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs); 579 for (i = 0; i < max_synth_devs; i++) { 580 snd_iprintf(buf, "\nsynth %d: ", i); 581 struct seq_oss_synth *rec __free(seq_oss_synth) = 582 get_sdev(i); 583 if (rec == NULL) { 584 snd_iprintf(buf, "*empty*\n"); 585 continue; 586 } 587 snd_iprintf(buf, "[%s]\n", rec->name); 588 snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n", 589 rec->synth_type, rec->synth_subtype, 590 rec->nr_voices); 591 snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", 592 str_enabled_disabled((long)rec->oper.ioctl), 593 str_enabled_disabled((long)rec->oper.load_patch)); 594 } 595} 596#endif /* CONFIG_SND_PROC_FS */
~/
Read-only file preview.