18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) by Paul Barton-Davis 1998-1999 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* The low level driver for the WaveFront ICS2115 MIDI interface(s) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Note that there is also an MPU-401 emulation (actually, a UART-401 98c2ecf20Sopenharmony_ci * emulation) on the CS4232 on the Tropez and Tropez Plus. This code 108c2ecf20Sopenharmony_ci * has nothing to do with that interface at all. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The interface is essentially just a UART-401, but is has the 138c2ecf20Sopenharmony_ci * interesting property of supporting what Turtle Beach called 148c2ecf20Sopenharmony_ci * "Virtual MIDI" mode. In this mode, there are effectively *two* 158c2ecf20Sopenharmony_ci * MIDI buses accessible via the interface, one that is routed 168c2ecf20Sopenharmony_ci * solely to/from the external WaveFront synthesizer and the other 178c2ecf20Sopenharmony_ci * corresponding to the pin/socket connector used to link external 188c2ecf20Sopenharmony_ci * MIDI devices to the board. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * This driver fully supports this mode, allowing two distinct MIDI 218c2ecf20Sopenharmony_ci * busses to be used completely independently, giving 32 channels of 228c2ecf20Sopenharmony_ci * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI 238c2ecf20Sopenharmony_ci * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, 248c2ecf20Sopenharmony_ci * where `n' is the card number. Note that the device numbers may be 258c2ecf20Sopenharmony_ci * something other than 0 and 1 if the CS4232 UART/MPU-401 interface 268c2ecf20Sopenharmony_ci * is enabled. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Switching between the two is accomplished externally by the driver 298c2ecf20Sopenharmony_ci * using the two otherwise unused MIDI bytes. See the code for more details. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c) 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * The main reason to turn off Virtual MIDI mode is when you want to 348c2ecf20Sopenharmony_ci * tightly couple the WaveFront synth with an external MIDI 358c2ecf20Sopenharmony_ci * device. You won't be able to distinguish the source of any MIDI 368c2ecf20Sopenharmony_ci * data except via SysEx ID, but thats probably OK, since for the most 378c2ecf20Sopenharmony_ci * part, the WaveFront won't be sending any MIDI data at all. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * The main reason to turn on Virtual MIDI Mode is to provide two 408c2ecf20Sopenharmony_ci * completely independent 16-channel MIDI buses, one to the 418c2ecf20Sopenharmony_ci * WaveFront and one to any external MIDI devices. Given the 32 428c2ecf20Sopenharmony_ci * voice nature of the WaveFront, its pretty easy to find a use 438c2ecf20Sopenharmony_ci * for all 16 channels driving just that synth. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include <linux/io.h> 488c2ecf20Sopenharmony_ci#include <linux/init.h> 498c2ecf20Sopenharmony_ci#include <linux/time.h> 508c2ecf20Sopenharmony_ci#include <linux/wait.h> 518c2ecf20Sopenharmony_ci#include <sound/core.h> 528c2ecf20Sopenharmony_ci#include <sound/snd_wavefront.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline int 558c2ecf20Sopenharmony_ciwf_mpu_status (snd_wavefront_midi_t *midi) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return inb (midi->mpu_status_port); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline int 628c2ecf20Sopenharmony_ciinput_avail (snd_wavefront_midi_t *midi) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci return !(wf_mpu_status(midi) & INPUT_AVAIL); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline int 698c2ecf20Sopenharmony_cioutput_ready (snd_wavefront_midi_t *midi) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return !(wf_mpu_status(midi) & OUTPUT_READY); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline int 768c2ecf20Sopenharmony_ciread_data (snd_wavefront_midi_t *midi) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return inb (midi->mpu_data_port); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline void 838c2ecf20Sopenharmony_ciwrite_data (snd_wavefront_midi_t *midi, unsigned char byte) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci outb (byte, midi->mpu_data_port); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic snd_wavefront_midi_t * 908c2ecf20Sopenharmony_ciget_wavefront_midi (struct snd_rawmidi_substream *substream) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct snd_card *card; 948c2ecf20Sopenharmony_ci snd_wavefront_card_t *acard; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (substream == NULL || substream->rmidi == NULL) 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci card = substream->rmidi->card; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (card == NULL) 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (card->private_data == NULL) 1058c2ecf20Sopenharmony_ci return NULL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci acard = card->private_data; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return &acard->wavefront.midi; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi = &card->wavefront.midi; 1158c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 1168c2ecf20Sopenharmony_ci unsigned long flags; 1178c2ecf20Sopenharmony_ci unsigned char midi_byte; 1188c2ecf20Sopenharmony_ci int max = 256, mask = 1; 1198c2ecf20Sopenharmony_ci int timeout; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Its not OK to try to change the status of "virtuality" of 1228c2ecf20Sopenharmony_ci the MIDI interface while we're outputting stuff. See 1238c2ecf20Sopenharmony_ci snd_wavefront_midi_{enable,disable}_virtual () for the 1248c2ecf20Sopenharmony_ci other half of this. 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci The first loop attempts to flush any data from the 1278c2ecf20Sopenharmony_ci current output device, and then the second 1288c2ecf20Sopenharmony_ci emits the switch byte (if necessary), and starts 1298c2ecf20Sopenharmony_ci outputting data for the output device currently in use. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (midi->substream_output[midi->output_mpu] == NULL) { 1338c2ecf20Sopenharmony_ci goto __second; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci while (max > 0) { 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* XXX fix me - no hard timing loops allowed! */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for (timeout = 30000; timeout > 0; timeout--) { 1418c2ecf20Sopenharmony_ci if (output_ready (midi)) 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 1468c2ecf20Sopenharmony_ci if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { 1478c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 1488c2ecf20Sopenharmony_ci goto __second; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci if (output_ready (midi)) { 1518c2ecf20Sopenharmony_ci if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { 1528c2ecf20Sopenharmony_ci if (!midi->isvirtual || 1538c2ecf20Sopenharmony_ci (midi_byte != WF_INTERNAL_SWITCH && 1548c2ecf20Sopenharmony_ci midi_byte != WF_EXTERNAL_SWITCH)) 1558c2ecf20Sopenharmony_ci write_data(midi, midi_byte); 1568c2ecf20Sopenharmony_ci max--; 1578c2ecf20Sopenharmony_ci } else { 1588c2ecf20Sopenharmony_ci if (midi->istimer) { 1598c2ecf20Sopenharmony_ci if (--midi->istimer <= 0) 1608c2ecf20Sopenharmony_ci del_timer(&midi->timer); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; 1638c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 1648c2ecf20Sopenharmony_ci goto __second; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci __second: 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (midi->substream_output[!midi->output_mpu] == NULL) { 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci while (max > 0) { 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* XXX fix me - no hard timing loops allowed! */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci for (timeout = 30000; timeout > 0; timeout--) { 1848c2ecf20Sopenharmony_ci if (output_ready (midi)) 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 1898c2ecf20Sopenharmony_ci if (!midi->isvirtual) 1908c2ecf20Sopenharmony_ci mask = 0; 1918c2ecf20Sopenharmony_ci mpu = midi->output_mpu ^ mask; 1928c2ecf20Sopenharmony_ci mask = 0; /* don't invert the value from now */ 1938c2ecf20Sopenharmony_ci if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { 1948c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) 1988c2ecf20Sopenharmony_ci goto __timer; 1998c2ecf20Sopenharmony_ci if (output_ready (midi)) { 2008c2ecf20Sopenharmony_ci if (mpu != midi->output_mpu) { 2018c2ecf20Sopenharmony_ci write_data(midi, mpu == internal_mpu ? 2028c2ecf20Sopenharmony_ci WF_INTERNAL_SWITCH : 2038c2ecf20Sopenharmony_ci WF_EXTERNAL_SWITCH); 2048c2ecf20Sopenharmony_ci midi->output_mpu = mpu; 2058c2ecf20Sopenharmony_ci } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { 2068c2ecf20Sopenharmony_ci if (!midi->isvirtual || 2078c2ecf20Sopenharmony_ci (midi_byte != WF_INTERNAL_SWITCH && 2088c2ecf20Sopenharmony_ci midi_byte != WF_EXTERNAL_SWITCH)) 2098c2ecf20Sopenharmony_ci write_data(midi, midi_byte); 2108c2ecf20Sopenharmony_ci max--; 2118c2ecf20Sopenharmony_ci } else { 2128c2ecf20Sopenharmony_ci __timer: 2138c2ecf20Sopenharmony_ci if (midi->istimer) { 2148c2ecf20Sopenharmony_ci if (--midi->istimer <= 0) 2158c2ecf20Sopenharmony_ci del_timer(&midi->timer); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; 2188c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned long flags; 2328c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 2338c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream || !substream->rmidi)) 2368c2ecf20Sopenharmony_ci return -ENXIO; 2378c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream->rmidi->private_data)) 2388c2ecf20Sopenharmony_ci return -ENXIO; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) 2438c2ecf20Sopenharmony_ci return -EIO; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->open, flags); 2468c2ecf20Sopenharmony_ci midi->mode[mpu] |= MPU401_MODE_INPUT; 2478c2ecf20Sopenharmony_ci midi->substream_input[mpu] = substream; 2488c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->open, flags); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned long flags; 2568c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 2578c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream || !substream->rmidi)) 2608c2ecf20Sopenharmony_ci return -ENXIO; 2618c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream->rmidi->private_data)) 2628c2ecf20Sopenharmony_ci return -ENXIO; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) 2678c2ecf20Sopenharmony_ci return -EIO; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->open, flags); 2708c2ecf20Sopenharmony_ci midi->mode[mpu] |= MPU401_MODE_OUTPUT; 2718c2ecf20Sopenharmony_ci midi->substream_output[mpu] = substream; 2728c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->open, flags); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci unsigned long flags; 2808c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 2818c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream || !substream->rmidi)) 2848c2ecf20Sopenharmony_ci return -ENXIO; 2858c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream->rmidi->private_data)) 2868c2ecf20Sopenharmony_ci return -ENXIO; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) 2918c2ecf20Sopenharmony_ci return -EIO; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->open, flags); 2948c2ecf20Sopenharmony_ci midi->mode[mpu] &= ~MPU401_MODE_INPUT; 2958c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->open, flags); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned long flags; 3038c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 3048c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream || !substream->rmidi)) 3078c2ecf20Sopenharmony_ci return -ENXIO; 3088c2ecf20Sopenharmony_ci if (snd_BUG_ON(!substream->rmidi->private_data)) 3098c2ecf20Sopenharmony_ci return -ENXIO; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) 3148c2ecf20Sopenharmony_ci return -EIO; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->open, flags); 3178c2ecf20Sopenharmony_ci midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; 3188c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->open, flags); 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci unsigned long flags; 3258c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 3268c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (substream == NULL || substream->rmidi == NULL) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (substream->rmidi->private_data == NULL) 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) { 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 3418c2ecf20Sopenharmony_ci if (up) { 3428c2ecf20Sopenharmony_ci midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; 3438c2ecf20Sopenharmony_ci } else { 3448c2ecf20Sopenharmony_ci midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_timer(struct timer_list *t) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi = from_timer(midi, t, timer); 3528c2ecf20Sopenharmony_ci snd_wavefront_card_t *card = midi->timer_card; 3538c2ecf20Sopenharmony_ci unsigned long flags; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 3568c2ecf20Sopenharmony_ci mod_timer(&midi->timer, 1 + jiffies); 3578c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 3588c2ecf20Sopenharmony_ci snd_wavefront_midi_output_write(card); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned long flags; 3648c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 3658c2ecf20Sopenharmony_ci snd_wavefront_mpu_id mpu; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (substream == NULL || substream->rmidi == NULL) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (substream->rmidi->private_data == NULL) 3718c2ecf20Sopenharmony_ci return; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if ((midi = get_wavefront_midi (substream)) == NULL) { 3768c2ecf20Sopenharmony_ci return; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 3808c2ecf20Sopenharmony_ci if (up) { 3818c2ecf20Sopenharmony_ci if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { 3828c2ecf20Sopenharmony_ci if (!midi->istimer) { 3838c2ecf20Sopenharmony_ci timer_setup(&midi->timer, 3848c2ecf20Sopenharmony_ci snd_wavefront_midi_output_timer, 3858c2ecf20Sopenharmony_ci 0); 3868c2ecf20Sopenharmony_ci mod_timer(&midi->timer, 1 + jiffies); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci midi->istimer++; 3898c2ecf20Sopenharmony_ci midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (up) 3978c2ecf20Sopenharmony_ci snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid 4018c2ecf20Sopenharmony_cisnd_wavefront_midi_interrupt (snd_wavefront_card_t *card) 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci unsigned long flags; 4058c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 4068c2ecf20Sopenharmony_ci static struct snd_rawmidi_substream *substream = NULL; 4078c2ecf20Sopenharmony_ci static int mpu = external_mpu; 4088c2ecf20Sopenharmony_ci int max = 128; 4098c2ecf20Sopenharmony_ci unsigned char byte; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci midi = &card->wavefront.midi; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!input_avail (midi)) { /* not for us */ 4148c2ecf20Sopenharmony_ci snd_wavefront_midi_output_write(card); 4158c2ecf20Sopenharmony_ci return; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci spin_lock_irqsave (&midi->virtual, flags); 4198c2ecf20Sopenharmony_ci while (--max) { 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (input_avail (midi)) { 4228c2ecf20Sopenharmony_ci byte = read_data (midi); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (midi->isvirtual) { 4258c2ecf20Sopenharmony_ci if (byte == WF_EXTERNAL_SWITCH) { 4268c2ecf20Sopenharmony_ci substream = midi->substream_input[external_mpu]; 4278c2ecf20Sopenharmony_ci mpu = external_mpu; 4288c2ecf20Sopenharmony_ci } else if (byte == WF_INTERNAL_SWITCH) { 4298c2ecf20Sopenharmony_ci substream = midi->substream_output[internal_mpu]; 4308c2ecf20Sopenharmony_ci mpu = internal_mpu; 4318c2ecf20Sopenharmony_ci } /* else just leave it as it is */ 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci substream = midi->substream_input[internal_mpu]; 4348c2ecf20Sopenharmony_ci mpu = internal_mpu; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (substream == NULL) { 4388c2ecf20Sopenharmony_ci continue; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { 4428c2ecf20Sopenharmony_ci snd_rawmidi_receive(substream, &byte, 1); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci } else { 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&midi->virtual, flags); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci snd_wavefront_midi_output_write(card); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_civoid 4548c2ecf20Sopenharmony_cisnd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci unsigned long flags; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci spin_lock_irqsave (&card->wavefront.midi.virtual, flags); 4608c2ecf20Sopenharmony_ci card->wavefront.midi.isvirtual = 1; 4618c2ecf20Sopenharmony_ci card->wavefront.midi.output_mpu = internal_mpu; 4628c2ecf20Sopenharmony_ci card->wavefront.midi.input_mpu = internal_mpu; 4638c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_civoid 4678c2ecf20Sopenharmony_cisnd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci unsigned long flags; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci spin_lock_irqsave (&card->wavefront.midi.virtual, flags); 4738c2ecf20Sopenharmony_ci // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); 4748c2ecf20Sopenharmony_ci // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); 4758c2ecf20Sopenharmony_ci card->wavefront.midi.isvirtual = 0; 4768c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ciint 4808c2ecf20Sopenharmony_cisnd_wavefront_midi_start (snd_wavefront_card_t *card) 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci int ok, i; 4848c2ecf20Sopenharmony_ci unsigned char rbuf[4], wbuf[4]; 4858c2ecf20Sopenharmony_ci snd_wavefront_t *dev; 4868c2ecf20Sopenharmony_ci snd_wavefront_midi_t *midi; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci dev = &card->wavefront; 4898c2ecf20Sopenharmony_ci midi = &dev->midi; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* The ICS2115 MPU-401 interface doesn't do anything 4928c2ecf20Sopenharmony_ci until its set into UART mode. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* XXX fix me - no hard timing loops allowed! */ 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (i = 0; i < 30000 && !output_ready (midi); i++); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (!output_ready (midi)) { 5008c2ecf20Sopenharmony_ci snd_printk ("MIDI interface not ready for command\n"); 5018c2ecf20Sopenharmony_ci return -1; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Any interrupts received from now on 5058c2ecf20Sopenharmony_ci are owned by the MIDI side of things. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dev->interrupts_are_midi = 1; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci outb (UART_MODE_ON, midi->mpu_command_port); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci for (ok = 0, i = 50000; i > 0 && !ok; i--) { 5138c2ecf20Sopenharmony_ci if (input_avail (midi)) { 5148c2ecf20Sopenharmony_ci if (read_data (midi) == MPU_ACK) { 5158c2ecf20Sopenharmony_ci ok = 1; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!ok) { 5228c2ecf20Sopenharmony_ci snd_printk ("cannot set UART mode for MIDI interface"); 5238c2ecf20Sopenharmony_ci dev->interrupts_are_midi = 0; 5248c2ecf20Sopenharmony_ci return -1; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Route external MIDI to WaveFront synth (by default) */ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { 5308c2ecf20Sopenharmony_ci snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); 5318c2ecf20Sopenharmony_ci /* XXX error ? */ 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Turn on Virtual MIDI, but first *always* turn it off, 5358c2ecf20Sopenharmony_ci since otherwise consecutive reloads of the driver will 5368c2ecf20Sopenharmony_ci never cause the hardware to generate the initial "internal" or 5378c2ecf20Sopenharmony_ci "external" source bytes in the MIDI data stream. This 5388c2ecf20Sopenharmony_ci is pretty important, since the internal hardware generally will 5398c2ecf20Sopenharmony_ci be used to generate none or very little MIDI output, and 5408c2ecf20Sopenharmony_ci thus the only source of MIDI data is actually external. Without 5418c2ecf20Sopenharmony_ci the switch bytes, the driver will think it all comes from 5428c2ecf20Sopenharmony_ci the internal interface. Duh. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { 5468c2ecf20Sopenharmony_ci snd_printk ("virtual MIDI mode not disabled\n"); 5478c2ecf20Sopenharmony_ci return 0; /* We're OK, but missing the external MIDI dev */ 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci snd_wavefront_midi_enable_virtual (card); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { 5538c2ecf20Sopenharmony_ci snd_printk ("cannot enable virtual MIDI mode.\n"); 5548c2ecf20Sopenharmony_ci snd_wavefront_midi_disable_virtual (card); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_output = 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci .open = snd_wavefront_midi_output_open, 5628c2ecf20Sopenharmony_ci .close = snd_wavefront_midi_output_close, 5638c2ecf20Sopenharmony_ci .trigger = snd_wavefront_midi_output_trigger, 5648c2ecf20Sopenharmony_ci}; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ciconst struct snd_rawmidi_ops snd_wavefront_midi_input = 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci .open = snd_wavefront_midi_input_open, 5698c2ecf20Sopenharmony_ci .close = snd_wavefront_midi_input_close, 5708c2ecf20Sopenharmony_ci .trigger = snd_wavefront_midi_input_trigger, 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 573