cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

opl3_seq.c (6828B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Uros Bizjak <uros@kss-loka.si>
      4 *
      5 *  Midi Sequencer interface routines for OPL2/OPL3/OPL4 FM
      6 *
      7 *  OPL2/3 FM instrument loader:
      8 *   alsa-tools/seq/sbiload/
      9 */
     10
     11#include "opl3_voice.h"
     12#include <linux/init.h>
     13#include <linux/moduleparam.h>
     14#include <linux/module.h>
     15#include <sound/initval.h>
     16
     17MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
     18MODULE_LICENSE("GPL");
     19MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
     20
     21bool use_internal_drums = 0;
     22module_param(use_internal_drums, bool, 0444);
     23MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
     24
     25int snd_opl3_synth_use_inc(struct snd_opl3 * opl3)
     26{
     27	if (!try_module_get(opl3->card->module))
     28		return -EFAULT;
     29	return 0;
     30
     31}
     32
     33void snd_opl3_synth_use_dec(struct snd_opl3 * opl3)
     34{
     35	module_put(opl3->card->module);
     36}
     37
     38int snd_opl3_synth_setup(struct snd_opl3 * opl3)
     39{
     40	int idx;
     41	struct snd_hwdep *hwdep = opl3->hwdep;
     42
     43	mutex_lock(&hwdep->open_mutex);
     44	if (hwdep->used) {
     45		mutex_unlock(&hwdep->open_mutex);
     46		return -EBUSY;
     47	}
     48	hwdep->used++;
     49	mutex_unlock(&hwdep->open_mutex);
     50
     51	snd_opl3_reset(opl3);
     52
     53	for (idx = 0; idx < MAX_OPL3_VOICES; idx++) {
     54		opl3->voices[idx].state = SNDRV_OPL3_ST_OFF;
     55		opl3->voices[idx].time = 0;
     56		opl3->voices[idx].keyon_reg = 0x00;
     57	}
     58	opl3->use_time = 0;
     59	opl3->connection_reg = 0x00;
     60	if (opl3->hardware >= OPL3_HW_OPL3) {
     61		/* Clear 4-op connections */
     62		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT,
     63				 opl3->connection_reg);
     64		opl3->max_voices = MAX_OPL3_VOICES;
     65	}
     66	return 0;
     67}
     68
     69void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
     70{
     71	unsigned long flags;
     72	struct snd_hwdep *hwdep;
     73
     74	/* Stop system timer */
     75	spin_lock_irqsave(&opl3->sys_timer_lock, flags);
     76	if (opl3->sys_timer_status) {
     77		del_timer(&opl3->tlist);
     78		opl3->sys_timer_status = 0;
     79	}
     80	spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
     81
     82	snd_opl3_reset(opl3);
     83	hwdep = opl3->hwdep;
     84	mutex_lock(&hwdep->open_mutex);
     85	hwdep->used--;
     86	mutex_unlock(&hwdep->open_mutex);
     87	wake_up(&hwdep->open_wait);
     88}
     89
     90static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info)
     91{
     92	struct snd_opl3 *opl3 = private_data;
     93	int err;
     94
     95	err = snd_opl3_synth_setup(opl3);
     96	if (err < 0)
     97		return err;
     98
     99	if (use_internal_drums) {
    100		/* Percussion mode */
    101		opl3->voices[6].state = opl3->voices[7].state = 
    102			opl3->voices[8].state = SNDRV_OPL3_ST_NOT_AVAIL;
    103		snd_opl3_load_drums(opl3);
    104		opl3->drum_reg = OPL3_PERCUSSION_ENABLE;
    105		opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg);
    106	} else {
    107		opl3->drum_reg = 0x00;
    108	}
    109
    110	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
    111		err = snd_opl3_synth_use_inc(opl3);
    112		if (err < 0)
    113			return err;
    114	}
    115	opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
    116	return 0;
    117}
    118
    119static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info)
    120{
    121	struct snd_opl3 *opl3 = private_data;
    122
    123	snd_opl3_synth_cleanup(opl3);
    124
    125	if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
    126		snd_opl3_synth_use_dec(opl3);
    127	return 0;
    128}
    129
    130/*
    131 * MIDI emulation operators
    132 */
    133const struct snd_midi_op opl3_ops = {
    134	.note_on =		snd_opl3_note_on,
    135	.note_off =		snd_opl3_note_off,
    136	.key_press =		snd_opl3_key_press,
    137	.note_terminate =	snd_opl3_terminate_note,
    138	.control =		snd_opl3_control,
    139	.nrpn =			snd_opl3_nrpn,
    140	.sysex =		snd_opl3_sysex,
    141};
    142
    143static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct,
    144				      void *private_data, int atomic, int hop)
    145{
    146	struct snd_opl3 *opl3 = private_data;
    147
    148	snd_midi_process_event(&opl3_ops, ev, opl3->chset);
    149	return 0;
    150}
    151
    152/* ------------------------------ */
    153
    154static void snd_opl3_synth_free_port(void *private_data)
    155{
    156	struct snd_opl3 *opl3 = private_data;
    157
    158	snd_midi_channel_free_set(opl3->chset);
    159}
    160
    161static int snd_opl3_synth_create_port(struct snd_opl3 * opl3)
    162{
    163	struct snd_seq_port_callback callbacks;
    164	char name[32];
    165	int voices, opl_ver;
    166
    167	voices = (opl3->hardware < OPL3_HW_OPL3) ?
    168		MAX_OPL2_VOICES : MAX_OPL3_VOICES;
    169	opl3->chset = snd_midi_channel_alloc_set(16);
    170	if (opl3->chset == NULL)
    171		return -ENOMEM;
    172	opl3->chset->private_data = opl3;
    173
    174	memset(&callbacks, 0, sizeof(callbacks));
    175	callbacks.owner = THIS_MODULE;
    176	callbacks.use = snd_opl3_synth_use;
    177	callbacks.unuse = snd_opl3_synth_unuse;
    178	callbacks.event_input = snd_opl3_synth_event_input;
    179	callbacks.private_free = snd_opl3_synth_free_port;
    180	callbacks.private_data = opl3;
    181
    182	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
    183	sprintf(name, "OPL%i FM Port", opl_ver);
    184
    185	opl3->chset->client = opl3->seq_client;
    186	opl3->chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
    187						      SNDRV_SEQ_PORT_CAP_WRITE |
    188						      SNDRV_SEQ_PORT_CAP_SUBS_WRITE,
    189						      SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
    190						      SNDRV_SEQ_PORT_TYPE_MIDI_GM |
    191						      SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE |
    192						      SNDRV_SEQ_PORT_TYPE_HARDWARE |
    193						      SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
    194						      16, voices,
    195						      name);
    196	if (opl3->chset->port < 0) {
    197		int port;
    198		port = opl3->chset->port;
    199		snd_midi_channel_free_set(opl3->chset);
    200		return port;
    201	}
    202	return 0;
    203}
    204
    205/* ------------------------------ */
    206
    207static int snd_opl3_seq_probe(struct device *_dev)
    208{
    209	struct snd_seq_device *dev = to_seq_dev(_dev);
    210	struct snd_opl3 *opl3;
    211	int client, err;
    212	char name[32];
    213	int opl_ver;
    214
    215	opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
    216	if (opl3 == NULL)
    217		return -EINVAL;
    218
    219	spin_lock_init(&opl3->voice_lock);
    220
    221	opl3->seq_client = -1;
    222
    223	/* allocate new client */
    224	opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
    225	sprintf(name, "OPL%i FM synth", opl_ver);
    226	client = opl3->seq_client =
    227		snd_seq_create_kernel_client(opl3->card, opl3->seq_dev_num,
    228					     name);
    229	if (client < 0)
    230		return client;
    231
    232	err = snd_opl3_synth_create_port(opl3);
    233	if (err < 0) {
    234		snd_seq_delete_kernel_client(client);
    235		opl3->seq_client = -1;
    236		return err;
    237	}
    238
    239	/* setup system timer */
    240	timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
    241	spin_lock_init(&opl3->sys_timer_lock);
    242	opl3->sys_timer_status = 0;
    243
    244#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
    245	snd_opl3_init_seq_oss(opl3, name);
    246#endif
    247	return 0;
    248}
    249
    250static int snd_opl3_seq_remove(struct device *_dev)
    251{
    252	struct snd_seq_device *dev = to_seq_dev(_dev);
    253	struct snd_opl3 *opl3;
    254
    255	opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev);
    256	if (opl3 == NULL)
    257		return -EINVAL;
    258
    259#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
    260	snd_opl3_free_seq_oss(opl3);
    261#endif
    262	if (opl3->seq_client >= 0) {
    263		snd_seq_delete_kernel_client(opl3->seq_client);
    264		opl3->seq_client = -1;
    265	}
    266	return 0;
    267}
    268
    269static struct snd_seq_driver opl3_seq_driver = {
    270	.driver = {
    271		.name = KBUILD_MODNAME,
    272		.probe = snd_opl3_seq_probe,
    273		.remove = snd_opl3_seq_remove,
    274	},
    275	.id = SNDRV_SEQ_DEV_ID_OPL3,
    276	.argsize = sizeof(struct snd_opl3 *),
    277};
    278
    279module_snd_seq_driver(opl3_seq_driver);