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

gus_uart.c (7332B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Routines for the GF1 MIDI interface - like UART 6850
      5 */
      6
      7#include <linux/delay.h>
      8#include <linux/interrupt.h>
      9#include <linux/time.h>
     10#include <sound/core.h>
     11#include <sound/gus.h>
     12
     13static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
     14{
     15	int count;
     16	unsigned char stat, byte;
     17	__always_unused unsigned char data;
     18	unsigned long flags;
     19
     20	count = 10;
     21	while (count) {
     22		spin_lock_irqsave(&gus->uart_cmd_lock, flags);
     23		stat = snd_gf1_uart_stat(gus);
     24		if (!(stat & 0x01)) {	/* data in Rx FIFO? */
     25			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     26			count--;
     27			continue;
     28		}
     29		count = 100;	/* arm counter to new value */
     30		data = snd_gf1_uart_get(gus);
     31		if (!(gus->gf1.uart_cmd & 0x80)) {
     32			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     33			continue;
     34		}			
     35		if (stat & 0x10) {	/* framing error */
     36			gus->gf1.uart_framing++;
     37			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     38			continue;
     39		}
     40		byte = snd_gf1_uart_get(gus);
     41		spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     42		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
     43		if (stat & 0x20) {
     44			gus->gf1.uart_overrun++;
     45		}
     46	}
     47}
     48
     49static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
     50{
     51	char byte;
     52	unsigned long flags;
     53
     54	/* try unlock output */
     55	if (snd_gf1_uart_stat(gus) & 0x01)
     56		snd_gf1_interrupt_midi_in(gus);
     57
     58	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
     59	if (snd_gf1_uart_stat(gus) & 0x02) {	/* Tx FIFO free? */
     60		if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {	/* no other bytes or error */
     61			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
     62		} else {
     63			snd_gf1_uart_put(gus, byte);
     64		}
     65	}
     66	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     67}
     68
     69static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
     70{
     71	snd_gf1_uart_cmd(gus, 0x03);	/* reset */
     72	if (!close && gus->uart_enable) {
     73		udelay(160);
     74		snd_gf1_uart_cmd(gus, 0x00);	/* normal operations */
     75	}
     76}
     77
     78static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
     79{
     80	unsigned long flags;
     81	struct snd_gus_card *gus;
     82
     83	gus = substream->rmidi->private_data;
     84	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
     85	if (!(gus->gf1.uart_cmd & 0x80)) {	/* input active? */
     86		snd_gf1_uart_reset(gus, 0);
     87	}
     88	gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
     89	gus->midi_substream_output = substream;
     90	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
     91#if 0
     92	snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
     93#endif
     94	return 0;
     95}
     96
     97static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
     98{
     99	unsigned long flags;
    100	struct snd_gus_card *gus;
    101	int i;
    102
    103	gus = substream->rmidi->private_data;
    104	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    105	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
    106		snd_gf1_uart_reset(gus, 0);
    107	}
    108	gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
    109	gus->midi_substream_input = substream;
    110	if (gus->uart_enable) {
    111		for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
    112			snd_gf1_uart_get(gus);	/* clean Rx */
    113		if (i >= 1000)
    114			snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
    115	}
    116	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    117#if 0
    118	snd_printk(KERN_DEBUG
    119		   "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
    120		   gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
    121	snd_printk(KERN_DEBUG
    122		   "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
    123		   "(page = 0x%x)\n",
    124		   gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
    125		   inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
    126#endif
    127	return 0;
    128}
    129
    130static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
    131{
    132	unsigned long flags;
    133	struct snd_gus_card *gus;
    134
    135	gus = substream->rmidi->private_data;
    136	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    137	if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
    138		snd_gf1_uart_reset(gus, 1);
    139	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
    140	gus->midi_substream_output = NULL;
    141	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    142	return 0;
    143}
    144
    145static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
    146{
    147	unsigned long flags;
    148	struct snd_gus_card *gus;
    149
    150	gus = substream->rmidi->private_data;
    151	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    152	if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
    153		snd_gf1_uart_reset(gus, 1);
    154	snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
    155	gus->midi_substream_input = NULL;
    156	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    157	return 0;
    158}
    159
    160static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
    161{
    162	struct snd_gus_card *gus;
    163	unsigned long flags;
    164
    165	gus = substream->rmidi->private_data;
    166
    167	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    168	if (up) {
    169		if ((gus->gf1.uart_cmd & 0x80) == 0)
    170			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
    171	} else {
    172		if (gus->gf1.uart_cmd & 0x80)
    173			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
    174	}
    175	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    176}
    177
    178static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
    179{
    180	unsigned long flags;
    181	struct snd_gus_card *gus;
    182	char byte;
    183	int timeout;
    184
    185	gus = substream->rmidi->private_data;
    186
    187	spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    188	if (up) {
    189		if ((gus->gf1.uart_cmd & 0x20) == 0) {
    190			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    191			/* wait for empty Rx - Tx is probably unlocked */
    192			timeout = 10000;
    193			while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
    194			/* Tx FIFO free? */
    195			spin_lock_irqsave(&gus->uart_cmd_lock, flags);
    196			if (gus->gf1.uart_cmd & 0x20) {
    197				spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    198				return;
    199			}
    200			if (snd_gf1_uart_stat(gus) & 0x02) {
    201				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
    202					spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    203					return;
    204				}
    205				snd_gf1_uart_put(gus, byte);
    206			}
    207			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);	/* enable Tx interrupt */
    208		}
    209	} else {
    210		if (gus->gf1.uart_cmd & 0x20)
    211			snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
    212	}
    213	spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
    214}
    215
    216static const struct snd_rawmidi_ops snd_gf1_uart_output =
    217{
    218	.open =		snd_gf1_uart_output_open,
    219	.close =	snd_gf1_uart_output_close,
    220	.trigger =	snd_gf1_uart_output_trigger,
    221};
    222
    223static const struct snd_rawmidi_ops snd_gf1_uart_input =
    224{
    225	.open =		snd_gf1_uart_input_open,
    226	.close =	snd_gf1_uart_input_close,
    227	.trigger =	snd_gf1_uart_input_trigger,
    228};
    229
    230int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
    231{
    232	struct snd_rawmidi *rmidi;
    233	int err;
    234
    235	err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
    236	if (err < 0)
    237		return err;
    238	strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
    239	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
    240	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
    241	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
    242	rmidi->private_data = gus;
    243	gus->midi_uart = rmidi;
    244	return err;
    245}