📄 wavefront_synth.c
字号:
err = 0; kfree(wc); return err; default: return -EINVAL; } return 0;}/***********************************************************************//* WaveFront: interface for card-level wavefront module *//***********************************************************************/voidsnd_wavefront_internal_interrupt (snd_wavefront_card_t *card){ snd_wavefront_t *dev = &card->wavefront; /* Some comments on interrupts. I attempted a version of this driver that used interrupts throughout the code instead of doing busy and/or sleep-waiting. Alas, it appears that once the Motorola firmware is downloaded, the card *never* generates an RX interrupt. These are successfully generated during firmware loading, and after that wavefront_status() reports that an interrupt is pending on the card from time to time, but it never seems to be delivered to this driver. Note also that wavefront_status() continues to report that RX interrupts are enabled, suggesting that I didn't goof up and disable them by mistake. Thus, I stepped back to a prior version of wavefront_wait(), the only place where this really matters. Its sad, but I've looked through the code to check on things, and I really feel certain that the Motorola firmware prevents RX-ready interrupts. */ if ((wavefront_status(dev) & (STAT_INTR_READ|STAT_INTR_WRITE)) == 0) { return; } spin_lock(&dev->irq_lock); dev->irq_ok = 1; dev->irq_cnt++; spin_unlock(&dev->irq_lock); wake_up(&dev->interrupt_sleeper);}/* STATUS REGISTER 0 Host Rx Interrupt Enable (1=Enabled)1 Host Rx Register Full (1=Full)2 Host Rx Interrupt Pending (1=Interrupt)3 Unused4 Host Tx Interrupt (1=Enabled)5 Host Tx Register empty (1=Empty)6 Host Tx Interrupt Pending (1=Interrupt)7 Unused*/static int __initsnd_wavefront_interrupt_bits (int irq){ int bits; switch (irq) { case 9: bits = 0x00; break; case 5: bits = 0x08; break; case 12: bits = 0x10; break; case 15: bits = 0x18; break; default: snd_printk ("invalid IRQ %d\n", irq); bits = -1; } return bits;}static void __initwavefront_should_cause_interrupt (snd_wavefront_t *dev, int val, int port, int timeout){ wait_queue_t wait; init_waitqueue_entry(&wait, current); spin_lock_irq(&dev->irq_lock); add_wait_queue(&dev->interrupt_sleeper, &wait); dev->irq_ok = 0; outb (val,port); spin_unlock_irq(&dev->irq_lock); while (1) { if ((timeout = schedule_timeout_interruptible(timeout)) == 0) return; if (dev->irq_ok) return; }}static int __initwavefront_reset_to_cleanliness (snd_wavefront_t *dev){ int bits; int hwv[2]; /* IRQ already checked */ bits = snd_wavefront_interrupt_bits (dev->irq); /* try reset of port */ outb (0x0, dev->control_port); /* At this point, the board is in reset, and the H/W initialization register is accessed at the same address as the data port. Bit 7 - Enable IRQ Driver 0 - Tri-state the Wave-Board drivers for the PC Bus IRQs 1 - Enable IRQ selected by bits 5:3 to be driven onto the PC Bus. Bit 6 - MIDI Interface Select 0 - Use the MIDI Input from the 26-pin WaveBlaster compatible header as the serial MIDI source 1 - Use the MIDI Input from the 9-pin D connector as the serial MIDI source. Bits 5:3 - IRQ Selection 0 0 0 - IRQ 2/9 0 0 1 - IRQ 5 0 1 0 - IRQ 12 0 1 1 - IRQ 15 1 0 0 - Reserved 1 0 1 - Reserved 1 1 0 - Reserved 1 1 1 - Reserved Bits 2:1 - Reserved Bit 0 - Disable Boot ROM 0 - memory accesses to 03FC30-03FFFFH utilize the internal Boot ROM 1 - memory accesses to 03FC30-03FFFFH are directed to external storage. */ /* configure hardware: IRQ, enable interrupts, plus external 9-pin MIDI interface selected */ outb (0x80 | 0x40 | bits, dev->data_port); /* CONTROL REGISTER 0 Host Rx Interrupt Enable (1=Enabled) 0x1 1 Unused 0x2 2 Unused 0x4 3 Unused 0x8 4 Host Tx Interrupt Enable 0x10 5 Mute (0=Mute; 1=Play) 0x20 6 Master Interrupt Enable (1=Enabled) 0x40 7 Master Reset (0=Reset; 1=Run) 0x80 Take us out of reset, mute output, master + TX + RX interrupts on. We'll get an interrupt presumably to tell us that the TX register is clear. */ wavefront_should_cause_interrupt(dev, 0x80|0x40|0x10|0x1, dev->control_port, (reset_time*HZ)/100); /* Note: data port is now the data port, not the h/w initialization port. */ if (!dev->irq_ok) { snd_printk ("intr not received after h/w un-reset.\n"); goto gone_bad; } /* Note: data port is now the data port, not the h/w initialization port. At this point, only "HW VERSION" or "DOWNLOAD OS" commands will work. So, issue one of them, and wait for TX interrupt. This can take a *long* time after a cold boot, while the ISC ROM does its RAM test. The SDK says up to 4 seconds - with 12MB of RAM on a Tropez+, it takes a lot longer than that (~16secs). Note that the card understands the difference between a warm and a cold boot, so subsequent ISC2115 reboots (say, caused by module reloading) will get through this much faster. XXX Interesting question: why is no RX interrupt received first ? */ wavefront_should_cause_interrupt(dev, WFC_HARDWARE_VERSION, dev->data_port, ramcheck_time*HZ); if (!dev->irq_ok) { snd_printk ("post-RAM-check interrupt not received.\n"); goto gone_bad; } if (!wavefront_wait (dev, STAT_CAN_READ)) { snd_printk ("no response to HW version cmd.\n"); goto gone_bad; } if ((hwv[0] = wavefront_read (dev)) == -1) { snd_printk ("board not responding correctly.\n"); goto gone_bad; } if (hwv[0] == 0xFF) { /* NAK */ /* Board's RAM test failed. Try to read error code, and tell us about it either way. */ if ((hwv[0] = wavefront_read (dev)) == -1) { snd_printk ("on-board RAM test failed " "(bad error code).\n"); } else { snd_printk ("on-board RAM test failed " "(error code: 0x%x).\n", hwv[0]); } goto gone_bad; } /* We're OK, just get the next byte of the HW version response */ if ((hwv[1] = wavefront_read (dev)) == -1) { snd_printk ("incorrect h/w response.\n"); goto gone_bad; } snd_printk ("hardware version %d.%d\n", hwv[0], hwv[1]); return 0; gone_bad: return (1);}#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/unistd.h>#include <linux/syscalls.h>#include <asm/uaccess.h>static int __initwavefront_download_firmware (snd_wavefront_t *dev, char *path){ unsigned char section[WF_SECTION_MAX]; signed char section_length; /* yes, just a char; max value is WF_SECTION_MAX */ int section_cnt_downloaded = 0; int fd; int c; int i; mm_segment_t fs; /* This tries to be a bit cleverer than the stuff Alan Cox did for the generic sound firmware, in that it actually knows something about the structure of the Motorola firmware. In particular, it uses a version that has been stripped of the 20K of useless header information, and had section lengths added, making it possible to load the entire OS without any [kv]malloc() activity, since the longest entity we ever read is 42 bytes (well, WF_SECTION_MAX) long. */ fs = get_fs(); set_fs (get_ds()); if ((fd = sys_open ((char __user *) path, 0, 0)) < 0) { snd_printk ("Unable to load \"%s\".\n", path); return 1; } while (1) { int x; if ((x = sys_read (fd, (char __user *) §ion_length, sizeof (section_length))) != sizeof (section_length)) { snd_printk ("firmware read error.\n"); goto failure; } if (section_length == 0) { break; } if (section_length < 0 || section_length > WF_SECTION_MAX) { snd_printk ("invalid firmware section length %d\n", section_length); goto failure; } if (sys_read (fd, (char __user *) section, section_length) != section_length) { snd_printk ("firmware section " "read error.\n"); goto failure; } /* Send command */ if (wavefront_write (dev, WFC_DOWNLOAD_OS)) { goto failure; } for (i = 0; i < section_length; i++) { if (wavefront_write (dev, section[i])) { goto failure; } } /* get ACK */ if (wavefront_wait (dev, STAT_CAN_READ)) { if ((c = inb (dev->data_port)) != WF_ACK) { snd_printk ("download " "of section #%d not " "acknowledged, ack = 0x%x\n", section_cnt_downloaded + 1, c); goto failure; } } else { snd_printk ("time out for firmware ACK.\n"); goto failure; } } sys_close (fd); set_fs (fs); return 0; failure: sys_close (fd); set_fs (fs); snd_printk ("firmware download failed!!!\n"); return 1;}static int __initwavefront_do_reset (snd_wavefront_t *dev){ char voices[1]; if (wavefront_reset_to_cleanliness (dev)) { snd_printk ("hw reset failed.\n"); goto gone_bad; } if (dev->israw) { if (wavefront_download_firmware (dev, ospath)) { goto gone_bad; } dev->israw = 0; /* Wait for the OS to get running. The protocol for this is non-obvious, and was determined by using port-IO tracing in DOSemu and some experimentation here. Rather than using timed waits, use interrupts creatively. */ wavefront_should_cause_interrupt (dev, WFC_NOOP, dev->data_port, (osrun_time*HZ)); if (!dev->irq_ok) { snd_printk ("no post-OS interrupt.\n"); goto gone_bad; } /* Now, do it again ! */ wavefront_should_cause_interrupt (dev, WFC_NOOP, dev->data_port, (10*HZ)); if (!dev->irq_ok) { snd_printk ("no post-OS interrupt(2).\n"); goto gone_bad; } /* OK, no (RX/TX) interrupts any more, but leave mute in effect. */ outb (0x80|0x40, dev->control_port); } /* SETUPSND.EXE asks for sample memory config here, but since i have no idea how to interpret the result, we'll forget about it. */ if ((dev->freemem = wavefront_freemem (dev)) < 0) { goto gone_bad; } snd_printk ("available DRAM %dk\n", dev->freemem / 1024); if (wavefront_write (dev, 0xf0) || wavefront_write (dev, 1) || (wavefront_read (dev) < 0)) { dev->debug = 0; snd_printk ("MPU emulation mode not set.\n"); goto gone_bad; } voices[0] = 32; if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, NULL, voices)) { snd_printk ("cannot set number of voices to 32.\n"); goto gone_bad; } return 0; gone_bad: /* reset that sucker so that it doesn't bother us. */ outb (0x0, dev->control_port); dev->interrupts_are_midi = 0; return 1;}int __initsnd_wavefront_start (snd_wavefront_t *dev){ int samples_are_from_rom; /* IMPORTANT: assumes that snd_wavefront_detect() and/or wavefront_reset_to_cleanliness() has already been called */ if (dev->israw) { samples_are_from_rom = 1; } else { /* XXX is this always true ? */ samples_are_from_rom = 0; } if (dev->israw || fx_raw) { if (wavefront_do_reset (dev)) { return -1; } } /* Check for FX device, present only on Tropez+ */ dev->has_fx = (snd_wavefront_fx_detect (dev) == 0); if (dev->has_fx && fx_raw) { snd_wavefront_fx_start (dev); } wavefront_get_sample_status (dev, samples_are_from_rom); wavefront_get_program_status (dev); wavefront_get_patch_status (dev); /* Start normal operation: unreset, master interrupt enabled, no mute */ outb (0x80|0x40|0x20, dev->control_port); return (0);}int __initsnd_wavefront_detect (snd_wavefront_card_t *card){ unsigned char rbuf[4], wbuf[4]; snd_wavefront_t *dev = &card->wavefront; /* returns zero if a WaveFront card is successfully detected. negative otherwise. */ dev->israw = 0; dev->has_fx = 0; dev->debug = debug_default; dev->interrupts_are_midi = 0; dev->irq_cnt = 0; dev->rom_samples_rdonly = 1; if (snd_wavefront_cmd (dev, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { dev->fw_version[0] = rbuf[0]; dev->fw_version[1] = rbuf[1]; snd_printk ("firmware %d.%d already loaded.\n", rbuf[0], rbuf[1]); /* check that a command actually works */ if (snd_wavefront_cmd (dev, WFC_HARDWARE_VERSION, rbuf, wbuf) == 0) { dev->hw_version[0] = rbuf[0]; dev->hw_version[1] = rbuf[1]; } else { snd_printk ("not raw, but no " "hardware version!\n"); return -1; } if (!wf_raw) { return 0; } else { snd_printk ("reloading firmware as you requested.\n"); dev->israw = 1; } } else { dev->israw = 1; snd_printk ("no response to firmware probe, assume raw.\n"); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -