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

mpu401_uart.c (15996B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Routines for control of MPU-401 in UART mode
      5 *
      6 *  MPU-401 supports UART mode which is not capable generate transmit
      7 *  interrupts thus output is done via polling. Without interrupt,
      8 *  input is done also via polling. Do not expect good performance.
      9 *
     10 *   13-03-2003:
     11 *      Added support for different kind of hardware I/O. Build in choices
     12 *      are port and mmio. For other kind of I/O, set mpu->read and
     13 *      mpu->write to your own I/O functions.
     14 */
     15
     16#include <linux/io.h>
     17#include <linux/delay.h>
     18#include <linux/init.h>
     19#include <linux/slab.h>
     20#include <linux/ioport.h>
     21#include <linux/module.h>
     22#include <linux/interrupt.h>
     23#include <linux/errno.h>
     24#include <sound/core.h>
     25#include <sound/mpu401.h>
     26
     27MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
     28MODULE_DESCRIPTION("Routines for control of MPU-401 in UART mode");
     29MODULE_LICENSE("GPL");
     30
     31static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu);
     32static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);
     33
     34/*
     35
     36 */
     37
     38#define snd_mpu401_input_avail(mpu) \
     39	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY))
     40#define snd_mpu401_output_ready(mpu) \
     41	(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL))
     42
     43/* Build in lowlevel io */
     44static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,
     45			      unsigned long addr)
     46{
     47	outb(data, addr);
     48}
     49
     50static unsigned char mpu401_read_port(struct snd_mpu401 *mpu,
     51				      unsigned long addr)
     52{
     53	return inb(addr);
     54}
     55
     56static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data,
     57			      unsigned long addr)
     58{
     59	writeb(data, (void __iomem *)addr);
     60}
     61
     62static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu,
     63				      unsigned long addr)
     64{
     65	return readb((void __iomem *)addr);
     66}
     67/*  */
     68
     69static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)
     70{
     71	int timeout = 100000;
     72	for (; timeout > 0 && snd_mpu401_input_avail(mpu); timeout--)
     73		mpu->read(mpu, MPU401D(mpu));
     74#ifdef CONFIG_SND_DEBUG
     75	if (timeout <= 0)
     76		snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n",
     77			   mpu->read(mpu, MPU401C(mpu)));
     78#endif
     79}
     80
     81static void uart_interrupt_tx(struct snd_mpu401 *mpu)
     82{
     83	unsigned long flags;
     84
     85	if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
     86	    test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
     87		spin_lock_irqsave(&mpu->output_lock, flags);
     88		snd_mpu401_uart_output_write(mpu);
     89		spin_unlock_irqrestore(&mpu->output_lock, flags);
     90	}
     91}
     92
     93static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
     94{
     95	unsigned long flags;
     96
     97	if (mpu->info_flags & MPU401_INFO_INPUT) {
     98		spin_lock_irqsave(&mpu->input_lock, flags);
     99		if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
    100			snd_mpu401_uart_input_read(mpu);
    101		else
    102			snd_mpu401_uart_clear_rx(mpu);
    103		spin_unlock_irqrestore(&mpu->input_lock, flags);
    104	}
    105	if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
    106		/* ok. for better Tx performance try do some output
    107		   when input is done */
    108		uart_interrupt_tx(mpu);
    109}
    110
    111/**
    112 * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler
    113 * @irq: the irq number
    114 * @dev_id: mpu401 instance
    115 *
    116 * Processes the interrupt for MPU401-UART i/o.
    117 *
    118 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
    119 */
    120irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)
    121{
    122	struct snd_mpu401 *mpu = dev_id;
    123	
    124	if (!mpu)
    125		return IRQ_NONE;
    126	_snd_mpu401_uart_interrupt(mpu);
    127	return IRQ_HANDLED;
    128}
    129
    130EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
    131
    132/**
    133 * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler
    134 * @irq: the irq number
    135 * @dev_id: mpu401 instance
    136 *
    137 * Processes the interrupt for MPU401-UART output.
    138 *
    139 * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
    140 */
    141irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)
    142{
    143	struct snd_mpu401 *mpu = dev_id;
    144	
    145	if (!mpu)
    146		return IRQ_NONE;
    147	uart_interrupt_tx(mpu);
    148	return IRQ_HANDLED;
    149}
    150
    151EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
    152
    153/*
    154 * timer callback
    155 * reprogram the timer and call the interrupt job
    156 */
    157static void snd_mpu401_uart_timer(struct timer_list *t)
    158{
    159	struct snd_mpu401 *mpu = from_timer(mpu, t, timer);
    160	unsigned long flags;
    161
    162	spin_lock_irqsave(&mpu->timer_lock, flags);
    163	/*mpu->mode |= MPU401_MODE_TIMER;*/
    164	mod_timer(&mpu->timer,  1 + jiffies);
    165	spin_unlock_irqrestore(&mpu->timer_lock, flags);
    166	if (mpu->rmidi)
    167		_snd_mpu401_uart_interrupt(mpu);
    168}
    169
    170/*
    171 * initialize the timer callback if not programmed yet
    172 */
    173static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
    174{
    175	unsigned long flags;
    176
    177	spin_lock_irqsave (&mpu->timer_lock, flags);
    178	if (mpu->timer_invoked == 0) {
    179		timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);
    180		mod_timer(&mpu->timer, 1 + jiffies);
    181	} 
    182	mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
    183		MPU401_MODE_OUTPUT_TIMER;
    184	spin_unlock_irqrestore (&mpu->timer_lock, flags);
    185}
    186
    187/*
    188 * remove the timer callback if still active
    189 */
    190static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
    191{
    192	unsigned long flags;
    193
    194	spin_lock_irqsave (&mpu->timer_lock, flags);
    195	if (mpu->timer_invoked) {
    196		mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :
    197			~MPU401_MODE_OUTPUT_TIMER;
    198		if (! mpu->timer_invoked)
    199			del_timer(&mpu->timer);
    200	}
    201	spin_unlock_irqrestore (&mpu->timer_lock, flags);
    202}
    203
    204/*
    205 * send a UART command
    206 * return zero if successful, non-zero for some errors
    207 */
    208
    209static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
    210			       int ack)
    211{
    212	unsigned long flags;
    213	int timeout, ok;
    214
    215	spin_lock_irqsave(&mpu->input_lock, flags);
    216	if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
    217		mpu->write(mpu, 0x00, MPU401D(mpu));
    218		/*snd_mpu401_uart_clear_rx(mpu);*/
    219	}
    220	/* ok. standard MPU-401 initialization */
    221	if (mpu->hardware != MPU401_HW_SB) {
    222		for (timeout = 1000; timeout > 0 &&
    223			     !snd_mpu401_output_ready(mpu); timeout--)
    224			udelay(10);
    225#ifdef CONFIG_SND_DEBUG
    226		if (!timeout)
    227			snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n",
    228				   mpu->read(mpu, MPU401C(mpu)));
    229#endif
    230	}
    231	mpu->write(mpu, cmd, MPU401C(mpu));
    232	if (ack && !(mpu->info_flags & MPU401_INFO_NO_ACK)) {
    233		ok = 0;
    234		timeout = 10000;
    235		while (!ok && timeout-- > 0) {
    236			if (snd_mpu401_input_avail(mpu)) {
    237				if (mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
    238					ok = 1;
    239			}
    240		}
    241		if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK)
    242			ok = 1;
    243	} else
    244		ok = 1;
    245	spin_unlock_irqrestore(&mpu->input_lock, flags);
    246	if (!ok) {
    247		snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx "
    248			   "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port,
    249			   mpu->read(mpu, MPU401C(mpu)),
    250			   mpu->read(mpu, MPU401D(mpu)));
    251		return 1;
    252	}
    253	return 0;
    254}
    255
    256static int snd_mpu401_do_reset(struct snd_mpu401 *mpu)
    257{
    258	if (snd_mpu401_uart_cmd(mpu, MPU401_RESET, 1))
    259		return -EIO;
    260	if (snd_mpu401_uart_cmd(mpu, MPU401_ENTER_UART, 0))
    261		return -EIO;
    262	return 0;
    263}
    264
    265/*
    266 * input/output open/close - protected by open_mutex in rawmidi.c
    267 */
    268static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)
    269{
    270	struct snd_mpu401 *mpu;
    271	int err;
    272
    273	mpu = substream->rmidi->private_data;
    274	if (mpu->open_input) {
    275		err = mpu->open_input(mpu);
    276		if (err < 0)
    277			return err;
    278	}
    279	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
    280		if (snd_mpu401_do_reset(mpu) < 0)
    281			goto error_out;
    282	}
    283	mpu->substream_input = substream;
    284	set_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
    285	return 0;
    286
    287error_out:
    288	if (mpu->open_input && mpu->close_input)
    289		mpu->close_input(mpu);
    290	return -EIO;
    291}
    292
    293static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)
    294{
    295	struct snd_mpu401 *mpu;
    296	int err;
    297
    298	mpu = substream->rmidi->private_data;
    299	if (mpu->open_output) {
    300		err = mpu->open_output(mpu);
    301		if (err < 0)
    302			return err;
    303	}
    304	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
    305		if (snd_mpu401_do_reset(mpu) < 0)
    306			goto error_out;
    307	}
    308	mpu->substream_output = substream;
    309	set_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
    310	return 0;
    311
    312error_out:
    313	if (mpu->open_output && mpu->close_output)
    314		mpu->close_output(mpu);
    315	return -EIO;
    316}
    317
    318static int snd_mpu401_uart_input_close(struct snd_rawmidi_substream *substream)
    319{
    320	struct snd_mpu401 *mpu;
    321	int err = 0;
    322
    323	mpu = substream->rmidi->private_data;
    324	clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode);
    325	mpu->substream_input = NULL;
    326	if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode))
    327		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
    328	if (mpu->close_input)
    329		mpu->close_input(mpu);
    330	if (err)
    331		return -EIO;
    332	return 0;
    333}
    334
    335static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)
    336{
    337	struct snd_mpu401 *mpu;
    338	int err = 0;
    339
    340	mpu = substream->rmidi->private_data;
    341	clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode);
    342	mpu->substream_output = NULL;
    343	if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
    344		err = snd_mpu401_uart_cmd(mpu, MPU401_RESET, 0);
    345	if (mpu->close_output)
    346		mpu->close_output(mpu);
    347	if (err)
    348		return -EIO;
    349	return 0;
    350}
    351
    352/*
    353 * trigger input callback
    354 */
    355static void
    356snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
    357{
    358	unsigned long flags;
    359	struct snd_mpu401 *mpu;
    360	int max = 64;
    361
    362	mpu = substream->rmidi->private_data;
    363	if (up) {
    364		if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER,
    365				       &mpu->mode)) {
    366			/* first time - flush FIFO */
    367			while (max-- > 0)
    368				mpu->read(mpu, MPU401D(mpu));
    369			if (mpu->info_flags & MPU401_INFO_USE_TIMER)
    370				snd_mpu401_uart_add_timer(mpu, 1);
    371		}
    372		
    373		/* read data in advance */
    374		spin_lock_irqsave(&mpu->input_lock, flags);
    375		snd_mpu401_uart_input_read(mpu);
    376		spin_unlock_irqrestore(&mpu->input_lock, flags);
    377	} else {
    378		if (mpu->info_flags & MPU401_INFO_USE_TIMER)
    379			snd_mpu401_uart_remove_timer(mpu, 1);
    380		clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
    381	}
    382
    383}
    384
    385/*
    386 * transfer input pending data
    387 * call with input_lock spinlock held
    388 */
    389static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)
    390{
    391	int max = 128;
    392	unsigned char byte;
    393
    394	while (max-- > 0) {
    395		if (! snd_mpu401_input_avail(mpu))
    396			break; /* input not available */
    397		byte = mpu->read(mpu, MPU401D(mpu));
    398		if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
    399			snd_rawmidi_receive(mpu->substream_input, &byte, 1);
    400	}
    401}
    402
    403/*
    404 *  Tx FIFO sizes:
    405 *    CS4237B			- 16 bytes
    406 *    AudioDrive ES1688         - 12 bytes
    407 *    S3 SonicVibes             -  8 bytes
    408 *    SoundBlaster AWE 64       -  2 bytes (ugly hardware)
    409 */
    410
    411/*
    412 * write output pending bytes
    413 * call with output_lock spinlock held
    414 */
    415static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
    416{
    417	unsigned char byte;
    418	int max = 256;
    419
    420	do {
    421		if (snd_rawmidi_transmit_peek(mpu->substream_output,
    422					      &byte, 1) == 1) {
    423			/*
    424			 * Try twice because there is hardware that insists on
    425			 * setting the output busy bit after each write.
    426			 */
    427			if (!snd_mpu401_output_ready(mpu) &&
    428			    !snd_mpu401_output_ready(mpu))
    429				break;	/* Tx FIFO full - try again later */
    430			mpu->write(mpu, byte, MPU401D(mpu));
    431			snd_rawmidi_transmit_ack(mpu->substream_output, 1);
    432		} else {
    433			snd_mpu401_uart_remove_timer (mpu, 0);
    434			break;	/* no other data - leave the tx loop */
    435		}
    436	} while (--max > 0);
    437}
    438
    439/*
    440 * output trigger callback
    441 */
    442static void
    443snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
    444{
    445	unsigned long flags;
    446	struct snd_mpu401 *mpu;
    447
    448	mpu = substream->rmidi->private_data;
    449	if (up) {
    450		set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
    451
    452		/* try to add the timer at each output trigger,
    453		 * since the output timer might have been removed in
    454		 * snd_mpu401_uart_output_write().
    455		 */
    456		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
    457			snd_mpu401_uart_add_timer(mpu, 0);
    458
    459		/* output pending data */
    460		spin_lock_irqsave(&mpu->output_lock, flags);
    461		snd_mpu401_uart_output_write(mpu);
    462		spin_unlock_irqrestore(&mpu->output_lock, flags);
    463	} else {
    464		if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
    465			snd_mpu401_uart_remove_timer(mpu, 0);
    466		clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode);
    467	}
    468}
    469
    470/*
    471
    472 */
    473
    474static const struct snd_rawmidi_ops snd_mpu401_uart_output =
    475{
    476	.open =		snd_mpu401_uart_output_open,
    477	.close =	snd_mpu401_uart_output_close,
    478	.trigger =	snd_mpu401_uart_output_trigger,
    479};
    480
    481static const struct snd_rawmidi_ops snd_mpu401_uart_input =
    482{
    483	.open =		snd_mpu401_uart_input_open,
    484	.close =	snd_mpu401_uart_input_close,
    485	.trigger =	snd_mpu401_uart_input_trigger,
    486};
    487
    488static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
    489{
    490	struct snd_mpu401 *mpu = rmidi->private_data;
    491	if (mpu->irq >= 0)
    492		free_irq(mpu->irq, (void *) mpu);
    493	release_and_free_resource(mpu->res);
    494	kfree(mpu);
    495}
    496
    497/**
    498 * snd_mpu401_uart_new - create an MPU401-UART instance
    499 * @card: the card instance
    500 * @device: the device index, zero-based
    501 * @hardware: the hardware type, MPU401_HW_XXXX
    502 * @port: the base address of MPU401 port
    503 * @info_flags: bitflags MPU401_INFO_XXX
    504 * @irq: the ISA irq number, -1 if not to be allocated
    505 * @rrawmidi: the pointer to store the new rawmidi instance
    506 *
    507 * Creates a new MPU-401 instance.
    508 *
    509 * Note that the rawmidi instance is returned on the rrawmidi argument,
    510 * not the mpu401 instance itself.  To access to the mpu401 instance,
    511 * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).
    512 *
    513 * Return: Zero if successful, or a negative error code.
    514 */
    515int snd_mpu401_uart_new(struct snd_card *card, int device,
    516			unsigned short hardware,
    517			unsigned long port,
    518			unsigned int info_flags,
    519			int irq,
    520			struct snd_rawmidi ** rrawmidi)
    521{
    522	struct snd_mpu401 *mpu;
    523	struct snd_rawmidi *rmidi;
    524	int in_enable, out_enable;
    525	int err;
    526
    527	if (rrawmidi)
    528		*rrawmidi = NULL;
    529	if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT)))
    530		info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
    531	in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
    532	out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
    533	err = snd_rawmidi_new(card, "MPU-401U", device,
    534			      out_enable, in_enable, &rmidi);
    535	if (err < 0)
    536		return err;
    537	mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
    538	if (!mpu) {
    539		err = -ENOMEM;
    540		goto free_device;
    541	}
    542	rmidi->private_data = mpu;
    543	rmidi->private_free = snd_mpu401_uart_free;
    544	spin_lock_init(&mpu->input_lock);
    545	spin_lock_init(&mpu->output_lock);
    546	spin_lock_init(&mpu->timer_lock);
    547	mpu->hardware = hardware;
    548	mpu->irq = -1;
    549	if (! (info_flags & MPU401_INFO_INTEGRATED)) {
    550		int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
    551		mpu->res = request_region(port, res_size, "MPU401 UART");
    552		if (!mpu->res) {
    553			snd_printk(KERN_ERR "mpu401_uart: "
    554				   "unable to grab port 0x%lx size %d\n",
    555				   port, res_size);
    556			err = -EBUSY;
    557			goto free_device;
    558		}
    559	}
    560	if (info_flags & MPU401_INFO_MMIO) {
    561		mpu->write = mpu401_write_mmio;
    562		mpu->read = mpu401_read_mmio;
    563	} else {
    564		mpu->write = mpu401_write_port;
    565		mpu->read = mpu401_read_port;
    566	}
    567	mpu->port = port;
    568	if (hardware == MPU401_HW_PC98II)
    569		mpu->cport = port + 2;
    570	else
    571		mpu->cport = port + 1;
    572	if (irq >= 0) {
    573		if (request_irq(irq, snd_mpu401_uart_interrupt, 0,
    574				"MPU401 UART", (void *) mpu)) {
    575			snd_printk(KERN_ERR "mpu401_uart: "
    576				   "unable to grab IRQ %d\n", irq);
    577			err = -EBUSY;
    578			goto free_device;
    579		}
    580	}
    581	if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))
    582		info_flags |= MPU401_INFO_USE_TIMER;
    583	mpu->info_flags = info_flags;
    584	mpu->irq = irq;
    585	if (card->shortname[0])
    586		snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
    587			 card->shortname);
    588	else
    589		sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device);
    590	if (out_enable) {
    591		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
    592				    &snd_mpu401_uart_output);
    593		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
    594	}
    595	if (in_enable) {
    596		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
    597				    &snd_mpu401_uart_input);
    598		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
    599		if (out_enable)
    600			rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
    601	}
    602	mpu->rmidi = rmidi;
    603	if (rrawmidi)
    604		*rrawmidi = rmidi;
    605	return 0;
    606free_device:
    607	snd_device_free(card, rmidi);
    608	return err;
    609}
    610
    611EXPORT_SYMBOL(snd_mpu401_uart_new);