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

emumpu401.c (10758B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Routines for control of EMU10K1 MPU-401 in UART mode
      5 */
      6
      7#include <linux/time.h>
      8#include <linux/init.h>
      9#include <sound/core.h>
     10#include <sound/emu10k1.h>
     11
     12#define EMU10K1_MIDI_MODE_INPUT		(1<<0)
     13#define EMU10K1_MIDI_MODE_OUTPUT	(1<<1)
     14
     15static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
     16					struct snd_emu10k1_midi *mpu, int idx)
     17{
     18	if (emu->audigy)
     19		return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
     20	else
     21		return inb(emu->port + mpu->port + idx);
     22}
     23
     24static inline void mpu401_write(struct snd_emu10k1 *emu,
     25				struct snd_emu10k1_midi *mpu, int data, int idx)
     26{
     27	if (emu->audigy)
     28		snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
     29	else
     30		outb(data, emu->port + mpu->port + idx);
     31}
     32
     33#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
     34#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
     35#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
     36#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
     37
     38#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
     39#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
     40
     41#define MPU401_RESET		0xff
     42#define MPU401_ENTER_UART	0x3f
     43#define MPU401_ACK		0xfe
     44
     45static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
     46{
     47	int timeout = 100000;
     48	for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
     49		mpu401_read_data(emu, mpu);
     50#ifdef CONFIG_SND_DEBUG
     51	if (timeout <= 0)
     52		dev_err(emu->card->dev,
     53			"cmd: clear rx timeout (status = 0x%x)\n",
     54			mpu401_read_stat(emu, mpu));
     55#endif
     56}
     57
     58/*
     59
     60 */
     61
     62static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
     63{
     64	unsigned char byte;
     65
     66	if (midi->rmidi == NULL) {
     67		snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
     68		return;
     69	}
     70
     71	spin_lock(&midi->input_lock);
     72	if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
     73		if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
     74			mpu401_clear_rx(emu, midi);
     75		} else {
     76			byte = mpu401_read_data(emu, midi);
     77			if (midi->substream_input)
     78				snd_rawmidi_receive(midi->substream_input, &byte, 1);
     79		}
     80	}
     81	spin_unlock(&midi->input_lock);
     82
     83	spin_lock(&midi->output_lock);
     84	if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
     85		if (midi->substream_output &&
     86		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
     87			mpu401_write_data(emu, midi, byte);
     88		} else {
     89			snd_emu10k1_intr_disable(emu, midi->tx_enable);
     90		}
     91	}
     92	spin_unlock(&midi->output_lock);
     93}
     94
     95static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
     96{
     97	do_emu10k1_midi_interrupt(emu, &emu->midi, status);
     98}
     99
    100static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
    101{
    102	do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
    103}
    104
    105static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
    106{
    107	unsigned long flags;
    108	int timeout, ok;
    109
    110	spin_lock_irqsave(&midi->input_lock, flags);
    111	mpu401_write_data(emu, midi, 0x00);
    112	/* mpu401_clear_rx(emu, midi); */
    113
    114	mpu401_write_cmd(emu, midi, cmd);
    115	if (ack) {
    116		ok = 0;
    117		timeout = 10000;
    118		while (!ok && timeout-- > 0) {
    119			if (mpu401_input_avail(emu, midi)) {
    120				if (mpu401_read_data(emu, midi) == MPU401_ACK)
    121					ok = 1;
    122			}
    123		}
    124		if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
    125			ok = 1;
    126	} else {
    127		ok = 1;
    128	}
    129	spin_unlock_irqrestore(&midi->input_lock, flags);
    130	if (!ok) {
    131		dev_err(emu->card->dev,
    132			"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
    133			   cmd, emu->port,
    134			   mpu401_read_stat(emu, midi),
    135			   mpu401_read_data(emu, midi));
    136		return 1;
    137	}
    138	return 0;
    139}
    140
    141static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
    142{
    143	struct snd_emu10k1 *emu;
    144	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    145	unsigned long flags;
    146
    147	emu = midi->emu;
    148	if (snd_BUG_ON(!emu))
    149		return -ENXIO;
    150	spin_lock_irqsave(&midi->open_lock, flags);
    151	midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
    152	midi->substream_input = substream;
    153	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
    154		spin_unlock_irqrestore(&midi->open_lock, flags);
    155		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
    156			goto error_out;
    157		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
    158			goto error_out;
    159	} else {
    160		spin_unlock_irqrestore(&midi->open_lock, flags);
    161	}
    162	return 0;
    163
    164error_out:
    165	return -EIO;
    166}
    167
    168static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
    169{
    170	struct snd_emu10k1 *emu;
    171	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    172	unsigned long flags;
    173
    174	emu = midi->emu;
    175	if (snd_BUG_ON(!emu))
    176		return -ENXIO;
    177	spin_lock_irqsave(&midi->open_lock, flags);
    178	midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
    179	midi->substream_output = substream;
    180	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
    181		spin_unlock_irqrestore(&midi->open_lock, flags);
    182		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
    183			goto error_out;
    184		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
    185			goto error_out;
    186	} else {
    187		spin_unlock_irqrestore(&midi->open_lock, flags);
    188	}
    189	return 0;
    190
    191error_out:
    192	return -EIO;
    193}
    194
    195static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
    196{
    197	struct snd_emu10k1 *emu;
    198	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    199	unsigned long flags;
    200	int err = 0;
    201
    202	emu = midi->emu;
    203	if (snd_BUG_ON(!emu))
    204		return -ENXIO;
    205	spin_lock_irqsave(&midi->open_lock, flags);
    206	snd_emu10k1_intr_disable(emu, midi->rx_enable);
    207	midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
    208	midi->substream_input = NULL;
    209	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
    210		spin_unlock_irqrestore(&midi->open_lock, flags);
    211		err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
    212	} else {
    213		spin_unlock_irqrestore(&midi->open_lock, flags);
    214	}
    215	return err;
    216}
    217
    218static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
    219{
    220	struct snd_emu10k1 *emu;
    221	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    222	unsigned long flags;
    223	int err = 0;
    224
    225	emu = midi->emu;
    226	if (snd_BUG_ON(!emu))
    227		return -ENXIO;
    228	spin_lock_irqsave(&midi->open_lock, flags);
    229	snd_emu10k1_intr_disable(emu, midi->tx_enable);
    230	midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
    231	midi->substream_output = NULL;
    232	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
    233		spin_unlock_irqrestore(&midi->open_lock, flags);
    234		err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
    235	} else {
    236		spin_unlock_irqrestore(&midi->open_lock, flags);
    237	}
    238	return err;
    239}
    240
    241static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
    242{
    243	struct snd_emu10k1 *emu;
    244	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    245	emu = midi->emu;
    246	if (snd_BUG_ON(!emu))
    247		return;
    248
    249	if (up)
    250		snd_emu10k1_intr_enable(emu, midi->rx_enable);
    251	else
    252		snd_emu10k1_intr_disable(emu, midi->rx_enable);
    253}
    254
    255static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
    256{
    257	struct snd_emu10k1 *emu;
    258	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
    259	unsigned long flags;
    260
    261	emu = midi->emu;
    262	if (snd_BUG_ON(!emu))
    263		return;
    264
    265	if (up) {
    266		int max = 4;
    267		unsigned char byte;
    268	
    269		/* try to send some amount of bytes here before interrupts */
    270		spin_lock_irqsave(&midi->output_lock, flags);
    271		while (max > 0) {
    272			if (mpu401_output_ready(emu, midi)) {
    273				if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
    274				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
    275					/* no more data */
    276					spin_unlock_irqrestore(&midi->output_lock, flags);
    277					return;
    278				}
    279				mpu401_write_data(emu, midi, byte);
    280				max--;
    281			} else {
    282				break;
    283			}
    284		}
    285		spin_unlock_irqrestore(&midi->output_lock, flags);
    286		snd_emu10k1_intr_enable(emu, midi->tx_enable);
    287	} else {
    288		snd_emu10k1_intr_disable(emu, midi->tx_enable);
    289	}
    290}
    291
    292/*
    293
    294 */
    295
    296static const struct snd_rawmidi_ops snd_emu10k1_midi_output =
    297{
    298	.open =		snd_emu10k1_midi_output_open,
    299	.close =	snd_emu10k1_midi_output_close,
    300	.trigger =	snd_emu10k1_midi_output_trigger,
    301};
    302
    303static const struct snd_rawmidi_ops snd_emu10k1_midi_input =
    304{
    305	.open =		snd_emu10k1_midi_input_open,
    306	.close =	snd_emu10k1_midi_input_close,
    307	.trigger =	snd_emu10k1_midi_input_trigger,
    308};
    309
    310static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
    311{
    312	struct snd_emu10k1_midi *midi = rmidi->private_data;
    313	midi->interrupt = NULL;
    314	midi->rmidi = NULL;
    315}
    316
    317static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
    318{
    319	struct snd_rawmidi *rmidi;
    320	int err;
    321
    322	err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
    323	if (err < 0)
    324		return err;
    325	midi->emu = emu;
    326	spin_lock_init(&midi->open_lock);
    327	spin_lock_init(&midi->input_lock);
    328	spin_lock_init(&midi->output_lock);
    329	strcpy(rmidi->name, name);
    330	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
    331	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
    332	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
    333	                     SNDRV_RAWMIDI_INFO_INPUT |
    334	                     SNDRV_RAWMIDI_INFO_DUPLEX;
    335	rmidi->private_data = midi;
    336	rmidi->private_free = snd_emu10k1_midi_free;
    337	midi->rmidi = rmidi;
    338	return 0;
    339}
    340
    341int snd_emu10k1_midi(struct snd_emu10k1 *emu)
    342{
    343	struct snd_emu10k1_midi *midi = &emu->midi;
    344	int err;
    345
    346	err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)");
    347	if (err < 0)
    348		return err;
    349
    350	midi->tx_enable = INTE_MIDITXENABLE;
    351	midi->rx_enable = INTE_MIDIRXENABLE;
    352	midi->port = MUDATA;
    353	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
    354	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
    355	midi->interrupt = snd_emu10k1_midi_interrupt;
    356	return 0;
    357}
    358
    359int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
    360{
    361	struct snd_emu10k1_midi *midi;
    362	int err;
    363
    364	midi = &emu->midi;
    365	err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)");
    366	if (err < 0)
    367		return err;
    368
    369	midi->tx_enable = INTE_MIDITXENABLE;
    370	midi->rx_enable = INTE_MIDIRXENABLE;
    371	midi->port = A_MUDATA1;
    372	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
    373	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
    374	midi->interrupt = snd_emu10k1_midi_interrupt;
    375
    376	midi = &emu->midi2;
    377	err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2");
    378	if (err < 0)
    379		return err;
    380
    381	midi->tx_enable = INTE_A_MIDITXENABLE2;
    382	midi->rx_enable = INTE_A_MIDIRXENABLE2;
    383	midi->port = A_MUDATA2;
    384	midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
    385	midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
    386	midi->interrupt = snd_emu10k1_midi_interrupt2;
    387	return 0;
    388}