diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /sound/isa/wavefront/wavefront_midi.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'sound/isa/wavefront/wavefront_midi.c')
-rw-r--r-- | sound/isa/wavefront/wavefront_midi.c | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c new file mode 100644 index 000000000..72e775ac7 --- /dev/null +++ b/sound/isa/wavefront/wavefront_midi.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) by Paul Barton-Davis 1998-1999 + */ + +/* The low level driver for the WaveFront ICS2115 MIDI interface(s) + * + * Note that there is also an MPU-401 emulation (actually, a UART-401 + * emulation) on the CS4232 on the Tropez and Tropez Plus. This code + * has nothing to do with that interface at all. + * + * The interface is essentially just a UART-401, but is has the + * interesting property of supporting what Turtle Beach called + * "Virtual MIDI" mode. In this mode, there are effectively *two* + * MIDI buses accessible via the interface, one that is routed + * solely to/from the external WaveFront synthesizer and the other + * corresponding to the pin/socket connector used to link external + * MIDI devices to the board. + * + * This driver fully supports this mode, allowing two distinct MIDI + * busses to be used completely independently, giving 32 channels of + * MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI + * bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1, + * where `n' is the card number. Note that the device numbers may be + * something other than 0 and 1 if the CS4232 UART/MPU-401 interface + * is enabled. + * + * Switching between the two is accomplished externally by the driver + * using the two otherwise unused MIDI bytes. See the code for more details. + * + * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c) + * + * The main reason to turn off Virtual MIDI mode is when you want to + * tightly couple the WaveFront synth with an external MIDI + * device. You won't be able to distinguish the source of any MIDI + * data except via SysEx ID, but thats probably OK, since for the most + * part, the WaveFront won't be sending any MIDI data at all. + * + * The main reason to turn on Virtual MIDI Mode is to provide two + * completely independent 16-channel MIDI buses, one to the + * WaveFront and one to any external MIDI devices. Given the 32 + * voice nature of the WaveFront, its pretty easy to find a use + * for all 16 channels driving just that synth. + * + */ + +#include <linux/io.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <sound/core.h> +#include <sound/snd_wavefront.h> + +static inline int +wf_mpu_status (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_status_port); +} + +static inline int +input_avail (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & INPUT_AVAIL); +} + +static inline int +output_ready (snd_wavefront_midi_t *midi) + +{ + return !(wf_mpu_status(midi) & OUTPUT_READY); +} + +static inline int +read_data (snd_wavefront_midi_t *midi) + +{ + return inb (midi->mpu_data_port); +} + +static inline void +write_data (snd_wavefront_midi_t *midi, unsigned char byte) + +{ + outb (byte, midi->mpu_data_port); +} + +static snd_wavefront_midi_t * +get_wavefront_midi (struct snd_rawmidi_substream *substream) + +{ + struct snd_card *card; + snd_wavefront_card_t *acard; + + if (substream == NULL || substream->rmidi == NULL) + return NULL; + + card = substream->rmidi->card; + + if (card == NULL) + return NULL; + + if (card->private_data == NULL) + return NULL; + + acard = card->private_data; + + return &acard->wavefront.midi; +} + +static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card) +{ + snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_mpu_id mpu; + unsigned long flags; + unsigned char midi_byte; + int max = 256, mask = 1; + int timeout; + + /* Its not OK to try to change the status of "virtuality" of + the MIDI interface while we're outputting stuff. See + snd_wavefront_midi_{enable,disable}_virtual () for the + other half of this. + + The first loop attempts to flush any data from the + current output device, and then the second + emits the switch byte (if necessary), and starts + outputting data for the output device currently in use. + */ + + if (midi->substream_output[midi->output_mpu] == NULL) { + goto __second; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + if (output_ready (midi)) { + if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + goto __second; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } + + __second: + + if (midi->substream_output[!midi->output_mpu] == NULL) { + return; + } + + while (max > 0) { + + /* XXX fix me - no hard timing loops allowed! */ + + for (timeout = 30000; timeout > 0; timeout--) { + if (output_ready (midi)) + break; + } + + spin_lock_irqsave (&midi->virtual, flags); + if (!midi->isvirtual) + mask = 0; + mpu = midi->output_mpu ^ mask; + mask = 0; /* don't invert the value from now */ + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + if (snd_rawmidi_transmit_empty(midi->substream_output[mpu])) + goto __timer; + if (output_ready (midi)) { + if (mpu != midi->output_mpu) { + write_data(midi, mpu == internal_mpu ? + WF_INTERNAL_SWITCH : + WF_EXTERNAL_SWITCH); + midi->output_mpu = mpu; + } else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) { + if (!midi->isvirtual || + (midi_byte != WF_INTERNAL_SWITCH && + midi_byte != WF_EXTERNAL_SWITCH)) + write_data(midi, midi_byte); + max--; + } else { + __timer: + if (midi->istimer) { + if (--midi->istimer <= 0) + del_timer(&midi->timer); + } + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + } else { + spin_unlock_irqrestore (&midi->virtual, flags); + return; + } + spin_unlock_irqrestore (&midi->virtual, flags); + } +} + +static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (snd_BUG_ON(!substream || !substream->rmidi)) + return -ENXIO; + if (snd_BUG_ON(!substream->rmidi->private_data)) + return -ENXIO; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_INPUT; + midi->substream_input[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (snd_BUG_ON(!substream || !substream->rmidi)) + return -ENXIO; + if (snd_BUG_ON(!substream->rmidi->private_data)) + return -ENXIO; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] |= MPU401_MODE_OUTPUT; + midi->substream_output[mpu] = substream; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (snd_BUG_ON(!substream || !substream->rmidi)) + return -ENXIO; + if (snd_BUG_ON(!substream->rmidi->private_data)) + return -ENXIO; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_INPUT; + spin_unlock_irqrestore (&midi->open, flags); + + return 0; +} + +static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (snd_BUG_ON(!substream || !substream->rmidi)) + return -ENXIO; + if (snd_BUG_ON(!substream->rmidi->private_data)) + return -ENXIO; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return -EIO; + + spin_lock_irqsave (&midi->open, flags); + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT; + spin_unlock_irqrestore (&midi->open, flags); + return 0; +} + +static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return; + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER; + } else { + midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); +} + +static void snd_wavefront_midi_output_timer(struct timer_list *t) +{ + snd_wavefront_midi_t *midi = from_timer(midi, t, timer); + snd_wavefront_card_t *card = midi->timer_card; + unsigned long flags; + + spin_lock_irqsave (&midi->virtual, flags); + mod_timer(&midi->timer, 1 + jiffies); + spin_unlock_irqrestore (&midi->virtual, flags); + snd_wavefront_midi_output_write(card); +} + +static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + snd_wavefront_mpu_id mpu; + + if (substream == NULL || substream->rmidi == NULL) + return; + + if (substream->rmidi->private_data == NULL) + return; + + mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); + + midi = get_wavefront_midi(substream); + if (!midi) + return; + + spin_lock_irqsave (&midi->virtual, flags); + if (up) { + if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { + if (!midi->istimer) { + timer_setup(&midi->timer, + snd_wavefront_midi_output_timer, + 0); + mod_timer(&midi->timer, 1 + jiffies); + } + midi->istimer++; + midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER; + } + } else { + midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER; + } + spin_unlock_irqrestore (&midi->virtual, flags); + + if (up) + snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data); +} + +void +snd_wavefront_midi_interrupt (snd_wavefront_card_t *card) + +{ + unsigned long flags; + snd_wavefront_midi_t *midi; + static struct snd_rawmidi_substream *substream = NULL; + static int mpu = external_mpu; + int max = 128; + unsigned char byte; + + midi = &card->wavefront.midi; + + if (!input_avail (midi)) { /* not for us */ + snd_wavefront_midi_output_write(card); + return; + } + + spin_lock_irqsave (&midi->virtual, flags); + while (--max) { + + if (input_avail (midi)) { + byte = read_data (midi); + + if (midi->isvirtual) { + if (byte == WF_EXTERNAL_SWITCH) { + substream = midi->substream_input[external_mpu]; + mpu = external_mpu; + } else if (byte == WF_INTERNAL_SWITCH) { + substream = midi->substream_output[internal_mpu]; + mpu = internal_mpu; + } /* else just leave it as it is */ + } else { + substream = midi->substream_input[internal_mpu]; + mpu = internal_mpu; + } + + if (substream == NULL) { + continue; + } + + if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) { + snd_rawmidi_receive(substream, &byte, 1); + } + } else { + break; + } + } + spin_unlock_irqrestore (&midi->virtual, flags); + + snd_wavefront_midi_output_write(card); +} + +void +snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + card->wavefront.midi.isvirtual = 1; + card->wavefront.midi.output_mpu = internal_mpu; + card->wavefront.midi.input_mpu = internal_mpu; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +void +snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card) + +{ + unsigned long flags; + + spin_lock_irqsave (&card->wavefront.midi.virtual, flags); + // snd_wavefront_midi_input_close (card->ics2115_external_rmidi); + // snd_wavefront_midi_output_close (card->ics2115_external_rmidi); + card->wavefront.midi.isvirtual = 0; + spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags); +} + +int +snd_wavefront_midi_start (snd_wavefront_card_t *card) + +{ + int ok, i; + unsigned char rbuf[4], wbuf[4]; + snd_wavefront_t *dev; + snd_wavefront_midi_t *midi; + + dev = &card->wavefront; + midi = &dev->midi; + + /* The ICS2115 MPU-401 interface doesn't do anything + until its set into UART mode. + */ + + /* XXX fix me - no hard timing loops allowed! */ + + for (i = 0; i < 30000 && !output_ready (midi); i++); + + if (!output_ready (midi)) { + snd_printk ("MIDI interface not ready for command\n"); + return -1; + } + + /* Any interrupts received from now on + are owned by the MIDI side of things. + */ + + dev->interrupts_are_midi = 1; + + outb (UART_MODE_ON, midi->mpu_command_port); + + for (ok = 0, i = 50000; i > 0 && !ok; i--) { + if (input_avail (midi)) { + if (read_data (midi) == MPU_ACK) { + ok = 1; + break; + } + } + } + + if (!ok) { + snd_printk ("cannot set UART mode for MIDI interface"); + dev->interrupts_are_midi = 0; + return -1; + } + + /* Route external MIDI to WaveFront synth (by default) */ + + if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) { + snd_printk ("can't enable MIDI-IN-2-synth routing.\n"); + /* XXX error ? */ + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consecutive reloads of the driver will + never cause the hardware to generate the initial "internal" or + "external" source bytes in the MIDI data stream. This + is pretty important, since the internal hardware generally will + be used to generate none or very little MIDI output, and + thus the only source of MIDI data is actually external. Without + the switch bytes, the driver will think it all comes from + the internal interface. Duh. + */ + + if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) { + snd_printk ("virtual MIDI mode not disabled\n"); + return 0; /* We're OK, but missing the external MIDI dev */ + } + + snd_wavefront_midi_enable_virtual (card); + + if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) { + snd_printk ("cannot enable virtual MIDI mode.\n"); + snd_wavefront_midi_disable_virtual (card); + } + return 0; +} + +const struct snd_rawmidi_ops snd_wavefront_midi_output = +{ + .open = snd_wavefront_midi_output_open, + .close = snd_wavefront_midi_output_close, + .trigger = snd_wavefront_midi_output_trigger, +}; + +const struct snd_rawmidi_ops snd_wavefront_midi_input = +{ + .open = snd_wavefront_midi_input_open, + .close = snd_wavefront_midi_input_close, + .trigger = snd_wavefront_midi_input_trigger, +}; + |