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

sb8_midi.c (7645B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Routines for control of SoundBlaster cards - MIDI interface
      5 *
      6 * --
      7 *
      8 * Sun May  9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
      9 *   Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from 
     10 *   working.
     11 *
     12 * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
     13 *   Added full duplex UART mode for DSP version 2.0 and later.
     14 */
     15
     16#include <linux/io.h>
     17#include <linux/time.h>
     18#include <sound/core.h>
     19#include <sound/sb.h>
     20
     21
     22irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
     23{
     24	struct snd_rawmidi *rmidi;
     25	int max = 64;
     26	char byte;
     27
     28	if (!chip)
     29		return IRQ_NONE;
     30	
     31	rmidi = chip->rmidi;
     32	if (!rmidi) {
     33		inb(SBP(chip, DATA_AVAIL));	/* ack interrupt */
     34		return IRQ_NONE;
     35	}
     36
     37	spin_lock(&chip->midi_input_lock);
     38	while (max-- > 0) {
     39		if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
     40			byte = inb(SBP(chip, READ));
     41			if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
     42				snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
     43			}
     44		}
     45	}
     46	spin_unlock(&chip->midi_input_lock);
     47	return IRQ_HANDLED;
     48}
     49
     50static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
     51{
     52	unsigned long flags;
     53	struct snd_sb *chip;
     54	unsigned int valid_open_flags;
     55
     56	chip = substream->rmidi->private_data;
     57	valid_open_flags = chip->hardware >= SB_HW_20
     58		? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
     59	spin_lock_irqsave(&chip->open_lock, flags);
     60	if (chip->open & ~valid_open_flags) {
     61		spin_unlock_irqrestore(&chip->open_lock, flags);
     62		return -EAGAIN;
     63	}
     64	chip->open |= SB_OPEN_MIDI_INPUT;
     65	chip->midi_substream_input = substream;
     66	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
     67		spin_unlock_irqrestore(&chip->open_lock, flags);
     68		snd_sbdsp_reset(chip);		/* reset DSP */
     69		if (chip->hardware >= SB_HW_20)
     70			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
     71	} else {
     72		spin_unlock_irqrestore(&chip->open_lock, flags);
     73	}
     74	return 0;
     75}
     76
     77static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
     78{
     79	unsigned long flags;
     80	struct snd_sb *chip;
     81	unsigned int valid_open_flags;
     82
     83	chip = substream->rmidi->private_data;
     84	valid_open_flags = chip->hardware >= SB_HW_20
     85		? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
     86	spin_lock_irqsave(&chip->open_lock, flags);
     87	if (chip->open & ~valid_open_flags) {
     88		spin_unlock_irqrestore(&chip->open_lock, flags);
     89		return -EAGAIN;
     90	}
     91	chip->open |= SB_OPEN_MIDI_OUTPUT;
     92	chip->midi_substream_output = substream;
     93	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
     94		spin_unlock_irqrestore(&chip->open_lock, flags);
     95		snd_sbdsp_reset(chip);		/* reset DSP */
     96		if (chip->hardware >= SB_HW_20)
     97			snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
     98	} else {
     99		spin_unlock_irqrestore(&chip->open_lock, flags);
    100	}
    101	return 0;
    102}
    103
    104static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
    105{
    106	unsigned long flags;
    107	struct snd_sb *chip;
    108
    109	chip = substream->rmidi->private_data;
    110	spin_lock_irqsave(&chip->open_lock, flags);
    111	chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
    112	chip->midi_substream_input = NULL;
    113	if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
    114		spin_unlock_irqrestore(&chip->open_lock, flags);
    115		snd_sbdsp_reset(chip);		/* reset DSP */
    116	} else {
    117		spin_unlock_irqrestore(&chip->open_lock, flags);
    118	}
    119	return 0;
    120}
    121
    122static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
    123{
    124	unsigned long flags;
    125	struct snd_sb *chip;
    126
    127	chip = substream->rmidi->private_data;
    128	del_timer_sync(&chip->midi_timer);
    129	spin_lock_irqsave(&chip->open_lock, flags);
    130	chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
    131	chip->midi_substream_output = NULL;
    132	if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
    133		spin_unlock_irqrestore(&chip->open_lock, flags);
    134		snd_sbdsp_reset(chip);		/* reset DSP */
    135	} else {
    136		spin_unlock_irqrestore(&chip->open_lock, flags);
    137	}
    138	return 0;
    139}
    140
    141static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
    142{
    143	unsigned long flags;
    144	struct snd_sb *chip;
    145
    146	chip = substream->rmidi->private_data;
    147	spin_lock_irqsave(&chip->open_lock, flags);
    148	if (up) {
    149		if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
    150			if (chip->hardware < SB_HW_20)
    151				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
    152			chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
    153		}
    154	} else {
    155		if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
    156			if (chip->hardware < SB_HW_20)
    157				snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
    158			chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
    159		}
    160	}
    161	spin_unlock_irqrestore(&chip->open_lock, flags);
    162}
    163
    164static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
    165{
    166	unsigned long flags;
    167	struct snd_sb *chip;
    168	char byte;
    169	int max = 32;
    170
    171	/* how big is Tx FIFO? */
    172	chip = substream->rmidi->private_data;
    173	while (max-- > 0) {
    174		spin_lock_irqsave(&chip->open_lock, flags);
    175		if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
    176			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
    177			del_timer(&chip->midi_timer);
    178			spin_unlock_irqrestore(&chip->open_lock, flags);
    179			break;
    180		}
    181		if (chip->hardware >= SB_HW_20) {
    182			int timeout = 8;
    183			while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
    184				;
    185			if (timeout == 0) {
    186				/* Tx FIFO full - try again later */
    187				spin_unlock_irqrestore(&chip->open_lock, flags);
    188				break;
    189			}
    190			outb(byte, SBP(chip, WRITE));
    191		} else {
    192			snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
    193			snd_sbdsp_command(chip, byte);
    194		}
    195		snd_rawmidi_transmit_ack(substream, 1);
    196		spin_unlock_irqrestore(&chip->open_lock, flags);
    197	}
    198}
    199
    200static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
    201{
    202	struct snd_sb *chip = from_timer(chip, t, midi_timer);
    203	struct snd_rawmidi_substream *substream = chip->midi_substream_output;
    204	unsigned long flags;
    205
    206	spin_lock_irqsave(&chip->open_lock, flags);
    207	mod_timer(&chip->midi_timer, 1 + jiffies);
    208	spin_unlock_irqrestore(&chip->open_lock, flags);	
    209	snd_sb8dsp_midi_output_write(substream);
    210}
    211
    212static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
    213{
    214	unsigned long flags;
    215	struct snd_sb *chip;
    216
    217	chip = substream->rmidi->private_data;
    218	spin_lock_irqsave(&chip->open_lock, flags);
    219	if (up) {
    220		if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
    221			mod_timer(&chip->midi_timer, 1 + jiffies);
    222			chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
    223		}
    224	} else {
    225		if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
    226			chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
    227		}
    228	}
    229	spin_unlock_irqrestore(&chip->open_lock, flags);
    230
    231	if (up)
    232		snd_sb8dsp_midi_output_write(substream);
    233}
    234
    235static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
    236{
    237	.open =		snd_sb8dsp_midi_output_open,
    238	.close =	snd_sb8dsp_midi_output_close,
    239	.trigger =	snd_sb8dsp_midi_output_trigger,
    240};
    241
    242static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
    243{
    244	.open =		snd_sb8dsp_midi_input_open,
    245	.close =	snd_sb8dsp_midi_input_close,
    246	.trigger =	snd_sb8dsp_midi_input_trigger,
    247};
    248
    249int snd_sb8dsp_midi(struct snd_sb *chip, int device)
    250{
    251	struct snd_rawmidi *rmidi;
    252	int err;
    253
    254	err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
    255	if (err < 0)
    256		return err;
    257	strcpy(rmidi->name, "SB8 MIDI");
    258	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
    259	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
    260	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
    261	if (chip->hardware >= SB_HW_20)
    262		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
    263	rmidi->private_data = chip;
    264	timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
    265	chip->rmidi = rmidi;
    266	return 0;
    267}