667 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OSS compatible sequencer driver4 *5 * synth device handlers6 *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 * constants20 */21#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 3022#define MAX_SYSEX_BUFLEN 12823 24 25/*26 * definition of synth info records27 */28 29/* sysex buffer */30struct seq_oss_synth_sysex {31 int len;32 int skip;33 unsigned char buf[MAX_SYSEX_BUFLEN];34};35 36/* synth info */37struct seq_oss_synth {38 int seq_device;39 40 /* for synth_info */41 int synth_type;42 int synth_subtype;43 int nr_voices;44 45 char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];46 struct snd_seq_oss_callback oper;47 48 int opened;49 50 void *private_data;51 snd_use_lock_t use_lock;52};53 54 55/*56 * device table57 */58static int max_synth_devs;59static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];60static struct seq_oss_synth midi_synth_dev = {61 .seq_device = -1,62 .synth_type = SYNTH_TYPE_MIDI,63 .synth_subtype = 0,64 .nr_voices = 16,65 .name = "MIDI",66};67 68static DEFINE_SPINLOCK(register_lock);69 70/*71 * prototypes72 */73static struct seq_oss_synth *get_synthdev(struct seq_oss_devinfo *dp, int dev);74static void reset_channels(struct seq_oss_synthinfo *info);75 76/*77 * global initialization78 */79void __init80snd_seq_oss_synth_init(void)81{82 snd_use_lock_init(&midi_synth_dev.use_lock);83}84 85/*86 * registration of the synth device87 */88int89snd_seq_oss_synth_probe(struct device *_dev)90{91 struct snd_seq_device *dev = to_seq_dev(_dev);92 int i;93 struct seq_oss_synth *rec;94 struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);95 unsigned long flags;96 97 rec = kzalloc(sizeof(*rec), GFP_KERNEL);98 if (!rec)99 return -ENOMEM;100 rec->seq_device = -1;101 rec->synth_type = reg->type;102 rec->synth_subtype = reg->subtype;103 rec->nr_voices = reg->nvoices;104 rec->oper = reg->oper;105 rec->private_data = reg->private_data;106 rec->opened = 0;107 snd_use_lock_init(&rec->use_lock);108 109 /* copy and truncate the name of synth device */110 strscpy(rec->name, dev->name, sizeof(rec->name));111 112 /* registration */113 spin_lock_irqsave(®ister_lock, flags);114 for (i = 0; i < max_synth_devs; i++) {115 if (synth_devs[i] == NULL)116 break;117 }118 if (i >= max_synth_devs) {119 if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {120 spin_unlock_irqrestore(®ister_lock, flags);121 pr_err("ALSA: seq_oss: no more synth slot\n");122 kfree(rec);123 return -ENOMEM;124 }125 max_synth_devs++;126 }127 rec->seq_device = i;128 synth_devs[i] = rec;129 spin_unlock_irqrestore(®ister_lock, flags);130 dev->driver_data = rec;131#ifdef SNDRV_OSS_INFO_DEV_SYNTH132 if (i < SNDRV_CARDS)133 snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);134#endif135 return 0;136}137 138 139int140snd_seq_oss_synth_remove(struct device *_dev)141{142 struct snd_seq_device *dev = to_seq_dev(_dev);143 int index;144 struct seq_oss_synth *rec = dev->driver_data;145 unsigned long flags;146 147 spin_lock_irqsave(®ister_lock, flags);148 for (index = 0; index < max_synth_devs; index++) {149 if (synth_devs[index] == rec)150 break;151 }152 if (index >= max_synth_devs) {153 spin_unlock_irqrestore(®ister_lock, flags);154 pr_err("ALSA: seq_oss: can't unregister synth\n");155 return -EINVAL;156 }157 synth_devs[index] = NULL;158 if (index == max_synth_devs - 1) {159 for (index--; index >= 0; index--) {160 if (synth_devs[index])161 break;162 }163 max_synth_devs = index + 1;164 }165 spin_unlock_irqrestore(®ister_lock, flags);166#ifdef SNDRV_OSS_INFO_DEV_SYNTH167 if (rec->seq_device < SNDRV_CARDS)168 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);169#endif170 171 snd_use_lock_sync(&rec->use_lock);172 kfree(rec);173 174 return 0;175}176 177 178/*179 */180static struct seq_oss_synth *181get_sdev(int dev)182{183 struct seq_oss_synth *rec;184 unsigned long flags;185 186 spin_lock_irqsave(®ister_lock, flags);187 rec = synth_devs[dev];188 if (rec)189 snd_use_lock_use(&rec->use_lock);190 spin_unlock_irqrestore(®ister_lock, flags);191 return rec;192}193 194 195/*196 * set up synth tables197 */198 199void200snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)201{202 int i;203 struct seq_oss_synth *rec;204 struct seq_oss_synthinfo *info;205 206 dp->max_synthdev = max_synth_devs;207 dp->synth_opened = 0;208 memset(dp->synths, 0, sizeof(dp->synths));209 for (i = 0; i < dp->max_synthdev; i++) {210 rec = get_sdev(i);211 if (rec == NULL)212 continue;213 if (rec->oper.open == NULL || rec->oper.close == NULL) {214 snd_use_lock_free(&rec->use_lock);215 continue;216 }217 info = &dp->synths[i];218 info->arg.app_index = dp->port;219 info->arg.file_mode = dp->file_mode;220 info->arg.seq_mode = dp->seq_mode;221 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)222 info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;223 else224 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;225 info->opened = 0;226 if (!try_module_get(rec->oper.owner)) {227 snd_use_lock_free(&rec->use_lock);228 continue;229 }230 if (rec->oper.open(&info->arg, rec->private_data) < 0) {231 module_put(rec->oper.owner);232 snd_use_lock_free(&rec->use_lock);233 continue;234 }235 info->nr_voices = rec->nr_voices;236 if (info->nr_voices > 0) {237 info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);238 if (!info->ch) {239 rec->oper.close(&info->arg);240 module_put(rec->oper.owner);241 snd_use_lock_free(&rec->use_lock);242 continue;243 }244 reset_channels(info);245 }246 info->opened++;247 rec->opened++;248 dp->synth_opened++;249 snd_use_lock_free(&rec->use_lock);250 }251}252 253 254/*255 * set up synth tables for MIDI emulation - /dev/music mode only256 */257 258void259snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp)260{261 int i;262 263 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)264 return;265 266 for (i = 0; i < dp->max_mididev; i++) {267 struct seq_oss_synthinfo *info;268 info = &dp->synths[dp->max_synthdev];269 if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)270 continue;271 info->arg.app_index = dp->port;272 info->arg.file_mode = dp->file_mode;273 info->arg.seq_mode = dp->seq_mode;274 info->arg.private_data = info;275 info->is_midi = 1;276 info->midi_mapped = i;277 info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;278 snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);279 info->opened = 1;280 midi_synth_dev.opened++;281 dp->max_synthdev++;282 if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)283 break;284 }285}286 287 288/*289 * clean up synth tables290 */291 292void293snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)294{295 int i;296 struct seq_oss_synth *rec;297 struct seq_oss_synthinfo *info;298 299 if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))300 return;301 for (i = 0; i < dp->max_synthdev; i++) {302 info = &dp->synths[i];303 if (! info->opened)304 continue;305 if (info->is_midi) {306 if (midi_synth_dev.opened > 0) {307 snd_seq_oss_midi_close(dp, info->midi_mapped);308 midi_synth_dev.opened--;309 }310 } else {311 rec = get_sdev(i);312 if (rec == NULL)313 continue;314 if (rec->opened > 0) {315 rec->oper.close(&info->arg);316 module_put(rec->oper.owner);317 rec->opened = 0;318 }319 snd_use_lock_free(&rec->use_lock);320 }321 kfree(info->sysex);322 info->sysex = NULL;323 kfree(info->ch);324 info->ch = NULL;325 }326 dp->synth_opened = 0;327 dp->max_synthdev = 0;328}329 330static struct seq_oss_synthinfo *331get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev)332{333 if (dev < 0 || dev >= dp->max_synthdev)334 return NULL;335 dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS);336 return &dp->synths[dev];337}338 339/*340 * return synth device information pointer341 */342static struct seq_oss_synth *343get_synthdev(struct seq_oss_devinfo *dp, int dev)344{345 struct seq_oss_synth *rec;346 struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);347 348 if (!info)349 return NULL;350 if (!info->opened)351 return NULL;352 if (info->is_midi) {353 rec = &midi_synth_dev;354 snd_use_lock_use(&rec->use_lock);355 } else {356 rec = get_sdev(dev);357 if (!rec)358 return NULL;359 }360 if (! rec->opened) {361 snd_use_lock_free(&rec->use_lock);362 return NULL;363 }364 return rec;365}366 367 368/*369 * reset note and velocity on each channel.370 */371static void372reset_channels(struct seq_oss_synthinfo *info)373{374 int i;375 if (info->ch == NULL || ! info->nr_voices)376 return;377 for (i = 0; i < info->nr_voices; i++) {378 info->ch[i].note = -1;379 info->ch[i].vel = 0;380 }381}382 383 384/*385 * reset synth device:386 * call reset callback. if no callback is defined, send a heartbeat387 * event to the corresponding port.388 */389void390snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)391{392 struct seq_oss_synth *rec;393 struct seq_oss_synthinfo *info;394 395 info = get_synthinfo_nospec(dp, dev);396 if (!info || !info->opened)397 return;398 if (info->sysex)399 info->sysex->len = 0; /* reset sysex */400 reset_channels(info);401 if (info->is_midi) {402 if (midi_synth_dev.opened <= 0)403 return;404 snd_seq_oss_midi_reset(dp, info->midi_mapped);405 /* reopen the device */406 snd_seq_oss_midi_close(dp, dev);407 if (snd_seq_oss_midi_open(dp, info->midi_mapped,408 dp->file_mode) < 0) {409 midi_synth_dev.opened--;410 info->opened = 0;411 kfree(info->sysex);412 info->sysex = NULL;413 kfree(info->ch);414 info->ch = NULL;415 }416 return;417 }418 419 rec = get_sdev(dev);420 if (rec == NULL)421 return;422 if (rec->oper.reset) {423 rec->oper.reset(&info->arg);424 } else {425 struct snd_seq_event ev;426 memset(&ev, 0, sizeof(ev));427 snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,428 info->arg.addr.port);429 ev.type = SNDRV_SEQ_EVENT_RESET;430 snd_seq_oss_dispatch(dp, &ev, 0, 0);431 }432 snd_use_lock_free(&rec->use_lock);433}434 435 436/*437 * load a patch record:438 * call load_patch callback function439 */440int441snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,442 const char __user *buf, int p, int c)443{444 struct seq_oss_synth *rec;445 struct seq_oss_synthinfo *info;446 int rc;447 448 info = get_synthinfo_nospec(dp, dev);449 if (!info)450 return -ENXIO;451 452 if (info->is_midi)453 return 0;454 rec = get_synthdev(dp, dev);455 if (!rec)456 return -ENXIO;457 458 if (rec->oper.load_patch == NULL)459 rc = -ENXIO;460 else461 rc = rec->oper.load_patch(&info->arg, fmt, buf, p, c);462 snd_use_lock_free(&rec->use_lock);463 return rc;464}465 466/*467 * check if the device is valid synth device and return the synth info468 */469struct seq_oss_synthinfo *470snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)471{472 struct seq_oss_synth *rec;473 474 rec = get_synthdev(dp, dev);475 if (rec) {476 snd_use_lock_free(&rec->use_lock);477 return get_synthinfo_nospec(dp, dev);478 }479 return NULL;480}481 482 483/*484 * receive OSS 6 byte sysex packet:485 * the full sysex message will be sent if it reaches to the end of data486 * (0xff).487 */488int489snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)490{491 int i, send;492 unsigned char *dest;493 struct seq_oss_synth_sysex *sysex;494 struct seq_oss_synthinfo *info;495 496 info = snd_seq_oss_synth_info(dp, dev);497 if (!info)498 return -ENXIO;499 500 sysex = info->sysex;501 if (sysex == NULL) {502 sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);503 if (sysex == NULL)504 return -ENOMEM;505 info->sysex = sysex;506 }507 508 send = 0;509 dest = sysex->buf + sysex->len;510 /* copy 6 byte packet to the buffer */511 for (i = 0; i < 6; i++) {512 if (buf[i] == 0xff) {513 send = 1;514 break;515 }516 dest[i] = buf[i];517 sysex->len++;518 if (sysex->len >= MAX_SYSEX_BUFLEN) {519 sysex->len = 0;520 sysex->skip = 1;521 break;522 }523 }524 525 if (sysex->len && send) {526 if (sysex->skip) {527 sysex->skip = 0;528 sysex->len = 0;529 return -EINVAL; /* skip */530 }531 /* copy the data to event record and send it */532 ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;533 if (snd_seq_oss_synth_addr(dp, dev, ev))534 return -EINVAL;535 ev->data.ext.len = sysex->len;536 ev->data.ext.ptr = sysex->buf;537 sysex->len = 0;538 return 0;539 }540 541 return -EINVAL; /* skip */542}543 544/*545 * fill the event source/destination addresses546 */547int548snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)549{550 struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev);551 552 if (!info)553 return -EINVAL;554 snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client,555 info->arg.addr.port);556 return 0;557}558 559 560/*561 * OSS compatible ioctl562 */563int564snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)565{566 struct seq_oss_synth *rec;567 struct seq_oss_synthinfo *info;568 int rc;569 570 info = get_synthinfo_nospec(dp, dev);571 if (!info || info->is_midi)572 return -ENXIO;573 rec = get_synthdev(dp, dev);574 if (!rec)575 return -ENXIO;576 if (rec->oper.ioctl == NULL)577 rc = -ENXIO;578 else579 rc = rec->oper.ioctl(&info->arg, cmd, addr);580 snd_use_lock_free(&rec->use_lock);581 return rc;582}583 584 585/*586 * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME587 */588int589snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)590{591 struct seq_oss_synthinfo *info;592 593 info = snd_seq_oss_synth_info(dp, dev);594 if (!info || info->is_midi)595 return -ENXIO;596 ev->type = SNDRV_SEQ_EVENT_OSS;597 memcpy(ev->data.raw8.d, data, 8);598 return snd_seq_oss_synth_addr(dp, dev, ev);599}600 601 602/*603 * create OSS compatible synth_info record604 */605int606snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf)607{608 struct seq_oss_synth *rec;609 struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);610 611 if (!info)612 return -ENXIO;613 614 if (info->is_midi) {615 struct midi_info minf;616 if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))617 return -ENXIO;618 inf->synth_type = SYNTH_TYPE_MIDI;619 inf->synth_subtype = 0;620 inf->nr_voices = 16;621 inf->device = dev;622 strscpy(inf->name, minf.name, sizeof(inf->name));623 } else {624 rec = get_synthdev(dp, dev);625 if (!rec)626 return -ENXIO;627 inf->synth_type = rec->synth_type;628 inf->synth_subtype = rec->synth_subtype;629 inf->nr_voices = rec->nr_voices;630 inf->device = dev;631 strscpy(inf->name, rec->name, sizeof(inf->name));632 snd_use_lock_free(&rec->use_lock);633 }634 return 0;635}636 637 638#ifdef CONFIG_SND_PROC_FS639/*640 * proc interface641 */642void643snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)644{645 int i;646 struct seq_oss_synth *rec;647 648 snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);649 for (i = 0; i < max_synth_devs; i++) {650 snd_iprintf(buf, "\nsynth %d: ", i);651 rec = get_sdev(i);652 if (rec == NULL) {653 snd_iprintf(buf, "*empty*\n");654 continue;655 }656 snd_iprintf(buf, "[%s]\n", rec->name);657 snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n",658 rec->synth_type, rec->synth_subtype,659 rec->nr_voices);660 snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",661 enabled_str((long)rec->oper.ioctl),662 enabled_str((long)rec->oper.load_patch));663 snd_use_lock_free(&rec->use_lock);664 }665}666#endif /* CONFIG_SND_PROC_FS */667