// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Routines for control of MPU-401 in UART mode * * MPU-401 supports UART mode which is not capable generate transmit * interrupts thus output is done via polling. Without interrupt, * input is done also via polling. Do not expect good performance. * * 13-03-2003: * Added support for different kind of hardware I/O. Build in choices * are port and mmio. For other kind of I/O, set mpu->read and * mpu->write to your own I/O functions.
*/
if (mpu->info_flags & MPU401_INFO_INPUT) {
spin_lock_irqsave(&mpu->input_lock, flags); if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
snd_mpu401_uart_input_read(mpu); else
snd_mpu401_uart_clear_rx(mpu);
spin_unlock_irqrestore(&mpu->input_lock, flags);
} if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) /* ok. for better Tx performance try do some output
when input is done */
uart_interrupt_tx(mpu);
}
/** * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler * @irq: the irq number * @dev_id: mpu401 instance * * Processes the interrupt for MPU401-UART i/o. * * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)
{ struct snd_mpu401 *mpu = dev_id;
if (!mpu) return IRQ_NONE;
_snd_mpu401_uart_interrupt(mpu); return IRQ_HANDLED;
}
EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
/** * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler * @irq: the irq number * @dev_id: mpu401 instance * * Processes the interrupt for MPU401-UART output. * * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)
{ struct snd_mpu401 *mpu = dev_id;
if (!mpu) return IRQ_NONE;
uart_interrupt_tx(mpu); return IRQ_HANDLED;
}
EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
/* * timer callback * reprogram the timer and call the interrupt job
*/ staticvoid snd_mpu401_uart_timer(struct timer_list *t)
{ struct snd_mpu401 *mpu = timer_container_of(mpu, t, timer); unsignedlong flags;
mpu = substream->rmidi->private_data;
clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
mpu->substream_output = NULL; if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0); if (mpu->close_output)
mpu->close_output(mpu); if (err) return -EIO; return 0;
}
/* * trigger input callback
*/ staticvoid
snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{ unsignedlong flags; struct snd_mpu401 *mpu; int max = 64;
mpu = substream->rmidi->private_data; if (up) { if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,
&mpu->mode)) { /* first time - flush FIFO */ while (max-- > 0)
mpu->read(mpu, MPU401D(mpu)); if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_add_timer(mpu, 1);
}
/* read data in advance */
spin_lock_irqsave(&mpu->input_lock, flags);
snd_mpu401_uart_input_read(mpu);
spin_unlock_irqrestore(&mpu->input_lock, flags);
} else { if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_remove_timer(mpu, 1);
clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
}
}
/* * transfer input pending data * call with input_lock spinlock held
*/ staticvoid snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)
{ int max = 128; unsignedchar byte;
while (max-- > 0) { if (! snd_mpu401_input_avail(mpu)) break; /* input not available */
byte = mpu->read(mpu, MPU401D(mpu)); if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
snd_rawmidi_receive(mpu->substream_input, &byte, 1);
}
}
/* * write output pending bytes * call with output_lock spinlock held
*/ staticvoid snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
{ unsignedchar byte; int max = 256;
do { if (snd_rawmidi_transmit_peek(mpu->substream_output,
&byte, 1) == 1) { /* * Try twice because there is hardware that insists on * setting the output busy bit after each write.
*/ if (!snd_mpu401_output_ready(mpu) &&
!snd_mpu401_output_ready(mpu)) break; /* Tx FIFO full - try again later */
mpu->write(mpu, byte, MPU401D(mpu));
snd_rawmidi_transmit_ack(mpu->substream_output, 1);
} else {
snd_mpu401_uart_remove_timer (mpu, 0); break; /* no other data - leave the tx loop */
}
} while (--max > 0);
}
mpu = substream->rmidi->private_data; if (up) {
set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
/* try to add the timer at each output trigger, * since the output timer might have been removed in * snd_mpu401_uart_output_write().
*/ if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
snd_mpu401_uart_add_timer(mpu, 0);
/** * snd_mpu401_uart_new - create an MPU401-UART instance * @card: the card instance * @device: the device index, zero-based * @hardware: the hardware type, MPU401_HW_XXXX * @port: the base address of MPU401 port * @info_flags: bitflags MPU401_INFO_XXX * @irq: the ISA irq number, -1 if not to be allocated * @rrawmidi: the pointer to store the new rawmidi instance * * Creates a new MPU-401 instance. * * Note that the rawmidi instance is returned on the rrawmidi argument, * not the mpu401 instance itself. To access to the mpu401 instance, * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). * * Return: Zero if successful, or a negative error code.
*/ int snd_mpu401_uart_new(struct snd_card *card, int device, unsignedshort hardware, unsignedlong port, unsignedint info_flags, int irq, struct snd_rawmidi ** rrawmidi)
{ struct snd_mpu401 *mpu; struct snd_rawmidi *rmidi; int in_enable, out_enable; int err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.