diff options
Diffstat (limited to 'drivers/sound/wavfront.c')
-rw-r--r-- | drivers/sound/wavfront.c | 3412 |
1 files changed, 3412 insertions, 0 deletions
diff --git a/drivers/sound/wavfront.c b/drivers/sound/wavfront.c new file mode 100644 index 000000000..735dcd93f --- /dev/null +++ b/drivers/sound/wavfront.c @@ -0,0 +1,3412 @@ +/* + * sound/wavefront.c + * + * A low level driver for Turtle Beach WaveFront Series + * (Maui, Tropez, Tropez Plus, and perhaps the Monterey & Rio) + * + * This driver supports the onboard wavetable synthesizer (an ICS2115), + * including patch, sample and program loading and unloading, conversion + * of GUS patches during loading, and full user-level access to all + * WaveFront commands. It tries to provide semi-intelligent patch and + * sample management as well. + * + * It also provides support for the ICS emulation of an MPU-401. Full + * support for the ICS emulation's "virtual MIDI mode" is provided in + * wf_midi.c. + * + * Support is also provided for the Tropez Plus' onboard FX processor, + * a Yamaha YSS225. Currently, code exists to configure the YSS225, + * and there is an interface allowing tweaking of any of its memory + * addresses. However, I have been unable to decipher the logical + * positioning of the configuration info for various effects, so for + * now, you just get the YSS225 in the same state as Turtle Beach's + * "SETUPSND.EXE" utility leaves it. + * + * The boards' CODEC (a Crystal CS4232) is supported by cs4232.[co], + * This chip also controls the configuration of the card: the wavefront + * synth is logical unit 4. + * + * NOTE: this driver has been written to support multiple WaveFront + * cards, but without using PnP to configure the CS4232, all of them + * would end up with the same configuration. Further, the current + * module loading interface doesn't permit this, since it only allows + * once instance of a module (which happens to be equivalent to a + * single hardware configuration) to be installed at one time.In + * addition, there is a hard limit on the available DMA channels that + * also makes installing more than 1 card limited to purely MIDI/synth + * activities on the second card. Still, the coding style gets rid of + * virtually all globals, which I believe is a better way to code + * device drivers (or anything else, for that matter). + * + ********************************************************************** + * + * Copyright (C) by Paul Barton-Davis 1998 + * + * Some portions of this file are taken from work that is + * copyright (C) by Hannu Savolainen 1993-1996 + * + * Although the relevant code here is all new, the handling of + * sample/alias/multi- samples is entirely based on a driver by Matt + * Martin and Rutger Nijlunsing which demonstrated how to get things + * to most aspects of this to work correctly. The GUS patch loading + * code has been almost unaltered by me, except to fit formatting and + * function names in the rest of the file. Many thanks to them. + * + * Appreciation and thanks to Hannu Savolainen for his early work on the Maui + * driver, and answering a few questions while this one was developed. + * + * Absolutely NO thanks to Turtle Beach/Voyetra and Yamaha for their + * complete lack of help in developing this driver, and in particular + * for their utter silence in response to questions about undocumented + * aspects of configuring a WaveFront soundcard, particularly the + * effects processor. + * + * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/init.h> + +#include "sound_config.h" +#include "soundmodule.h" + +#include <linux/wavefront.h> + +#define MIDI_SYNTH_NAME "WaveFront MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT +#include "midi_synth.h" + +#define COPY_FROM_USER(a,b,c) copy_from_user ((a),(b),(c)) +#define COPY_TO_USER(a,b,c) copy_to_user ((a),(b),(c)) + +#if defined(CONFIG_SOUND_WAVEFRONT) || defined(CONFIG_SOUND_WAVEFRONT_MODULE) + +/* This thing is meant to work as a module */ + +#ifdef MODULE + +/* bitmasks for WaveFront status port value */ + +#define STAT_INTR_WRITE 0x40 +#define STAT_CAN_WRITE 0x20 +#define STAT_RINTR_ENABLED 0x10 +#define STAT_INTR_READ 0x04 +#define STAT_CAN_READ 0x02 +#define STAT_WINTR_ENABLED 0x01 + +/*** Module-accessible parameters ***************************************/ + +int wf_raw = 0; /* we normally check for "raw state" to firmware + loading. if set, then during driver loading, the + state of the board is ignored, and we reset the + board and load the firmware anyway. + */ + +int fx_raw = 1; /* if this is zero, we'll leave the FX processor in + whatever state it is when the driver is loaded. + The default is to download the microprogram and + associated coefficients to set it up for "default" + operation, whatever that means. + */ + +int wf_debug_default = 0; /* you can set this to control debugging + during driver loading. it takes any combination + of the WF_DEBUG_* flags defined in + wavefront.h + */ + +/* XXX this needs to be made firmware and hardware version dependent */ + +char *wf_ospath = "/etc/sound/wavefront.os"; /* where to find a processed + version of the WaveFront OS + */ + +/* These three don't need to be messed with unless you're trying to + tweak the driver for optimal I/O performance. Read wavefront_wait() + and wavefront_sleep() to see what they do. You may need or want to + tweak them for CPU's different than the 486/66Mhz that I run on. +*/ + +int wf_short_wait_count = 5000; /* loops, CPU dependent */ +int wf_sleep_interval = 50; /* HZ/wf_sleep_interval seconds per sleep */ +int wf_sleep_tries = 100; /*2sec*/ /* number of times we'll try to sleep */ + +MODULE_PARM(wf_raw,"i"); +MODULE_PARM(fx_raw,"i"); +MODULE_PARM(wf_debug_default,"i"); +MODULE_PARM(wf_ospath,"s"); +MODULE_PARM(wf_short_wait_count,"i"); +MODULE_PARM(wf_sleep_interval,"i"); +MODULE_PARM(wf_sleep_tries,"i"); + +/***************************************************************************/ + +static struct synth_info wavefront_info = +{"Turtle Beach WaveFront", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_WAVEFRONT, + 0, 32, 0, 0, SYNTH_CAP_INPUT}; + +static int (*midi_load_patch) (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) = NULL; + + +typedef struct wf_config { + int installed; /* well, is it ? note: doesn't mean "working" */ + int devno; /* device number from kernel */ + int irq; /* "you were one, one of the few ..." */ + int base; /* low i/o port address */ + +#define mpu_data_port base +#define mpu_command_port base + 1 /* write semantics */ +#define mpu_status_port base + 1 /* read semantics */ +#define data_port base + 2 +#define status_port base + 3 /* read semantics */ +#define control_port base + 3 /* write semantics */ +#define block_port base + 4 /* 16 bit, writeonly */ +#define last_block_port base + 6 /* 16 bit, writeonly */ + + /* FX ports. These are mapped through the ICS2115 to the YS225. + The ICS2115 takes care of flipping the relevant pins on the + YS225 so that access to each of these ports does the right + thing. Note: these are NOT documented by Turtle Beach. + */ + +#define fx_status base + 8 +#define fx_op base + 8 +#define fx_lcr base + 9 +#define fx_dsp_addr base + 0xa +#define fx_dsp_page base + 0xb +#define fx_dsp_lsb base + 0xc +#define fx_dsp_msb base + 0xd +#define fx_mod_addr base + 0xe +#define fx_mod_data base + 0xf + + volatile int irq_ok; /* set by interrupt handler */ + int opened; /* flag, holds open(1) mode */ + char debug; /* debugging flags */ + unsigned int freemem; /* installed RAM, in bytes */ + int synthdev; /* OSS minor devnum for synth */ + int mididev; /* OSS minor devno for internal MIDI */ + int ext_mididev; /* OSS minor devno for external MIDI */ + char fw_version[2]; /* major = [0], minor = [1] */ + char hw_version[2]; /* major = [0], minor = [1] */ + char israw; /* needs Motorola microcode */ + char prog_status[WF_MAX_PROGRAM]; /* WF_SLOT_* */ + char patch_status[WF_MAX_PATCH]; /* WF_SLOT_* */ + char sample_status[WF_MAX_SAMPLE]; /* WF_ST_* | WF_SLOT_* */ + int samples_used; /* how many */ + char interrupts_on; /* h/w MPU interrupts enabled ? */ + char rom_samples_rdonly; /* can we write on ROM samples */ +} wf_config; + +static wf_config wfs[WAVEFRONT_MAX_DEVICES]; + +#define wavefront_status(hw) (inb (hw->status_port)) + +/* forward references */ + +static int wffx_ioctl (struct wf_config *, wavefront_fx_info *); +static int wffx_init (struct wf_config *hw); +static int wavefront_delete_sample (struct wf_config *hw, int sampnum); + +static volatile int irq2hw[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +static volatile int dev2hw[17] = +{-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +typedef struct { + int cmd; + char *action; + unsigned int read_cnt; + unsigned int write_cnt; + int need_ack; +} wavefront_command; + +static struct { + int errno; + const char *errstr; +} wavefront_errors[] = { + { 0x01, "Bad sample number" }, + { 0x02, "Out of sample memory" }, + { 0x03, "Bad patch number" }, + { 0x04, "Error in number of voices" }, + { 0x06, "Sample load already in progress" }, + { 0x0B, "No sample load request pending" }, + { 0x0E, "Bad MIDI channel number" }, + { 0x10, "Download Record Error" }, + { 0x80, "Success" }, + { 0x0, 0x0 } +}; + +#define NEEDS_ACK 1 + +static wavefront_command wavefront_commands[] = { + { WFC_SET_SYNTHVOL, "set synthesizer volume", 0, 1, NEEDS_ACK }, + { WFC_GET_SYNTHVOL, "get synthesizer volume", 1, 0, 0}, + { WFC_SET_NVOICES, "set number of voices", 0, 1, NEEDS_ACK }, + { WFC_GET_NVOICES, "get number of voices", 1, 0, 0 }, + { WFC_SET_TUNING, "set synthesizer tuning", 0, 2, NEEDS_ACK }, + { WFC_GET_TUNING, "get synthesizer tuning", 2, 0, 0 }, + { WFC_DISABLE_CHANNEL, "disable synth channel", 0, 1, NEEDS_ACK }, + { WFC_ENABLE_CHANNEL, "enable synth channel", 0, 1, NEEDS_ACK }, + { WFC_GET_CHANNEL_STATUS, "get synth channel status", 3, 0, 0 }, + { WFC_MISYNTH_OFF, "disable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_MISYNTH_ON, "enable midi-in to synth", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_ON, "enable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_VMIDI_OFF, "disable virtual midi mode", 0, 0, NEEDS_ACK }, + { WFC_MIDI_STATUS, "report midi status", 1, 0, 0 }, + { WFC_FIRMWARE_VERSION, "report firmware version", 2, 0, 0 }, + { WFC_HARDWARE_VERSION, "report hardware version", 2, 0, 0 }, + { WFC_GET_NSAMPLES, "report number of samples", 2, 0, 0 }, + { WFC_INSTOUT_LEVELS, "report instantaneous output levels", 7, 0, 0 }, + { WFC_PEAKOUT_LEVELS, "report peak output levels", 7, 0, 0 }, + { WFC_DOWNLOAD_SAMPLE, "download sample", + 0, WF_SAMPLE_BYTES, NEEDS_ACK }, + { WFC_DOWNLOAD_BLOCK, "download block", 0, 0, NEEDS_ACK}, + { WFC_DOWNLOAD_SAMPLE_HEADER, "download sample header", + 0, WF_SAMPLE_HDR_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_HEADER, "upload sample header", 13, 2, 0 }, + + /* This command requires a variable number of bytes to be written. + There is a hack in wavefront_cmd() to support this. The actual + count is passed in as the read buffer ptr, cast appropriately. + Ugh. + */ + + { WFC_DOWNLOAD_MULTISAMPLE, "download multisample", 0, 0, NEEDS_ACK }, + + /* This one is a hack as well. We just read the first byte of the + response, don't fetch an ACK, and leave the rest to the + calling function. Ugly, ugly, ugly. + */ + + { WFC_UPLOAD_MULTISAMPLE, "upload multisample", 2, 1, 0 }, + { WFC_DOWNLOAD_SAMPLE_ALIAS, "download sample alias", + 0, WF_ALIAS_BYTES, NEEDS_ACK }, + { WFC_UPLOAD_SAMPLE_ALIAS, "upload sample alias", WF_ALIAS_BYTES, 2, 0}, + { WFC_DELETE_SAMPLE, "delete sample", 0, 2, NEEDS_ACK }, + { WFC_IDENTIFY_SAMPLE_TYPE, "identify sample type", 5, 2, 0 }, + { WFC_UPLOAD_SAMPLE_PARAMS, "upload sample parameters" }, + { WFC_REPORT_FREE_MEMORY, "report free memory", 4, 0, 0 }, + { WFC_DOWNLOAD_PATCH, "download patch", 0, 134, NEEDS_ACK }, + { WFC_UPLOAD_PATCH, "upload patch", 132, 2, 0 }, + { WFC_DOWNLOAD_PROGRAM, "download program", 0, 33, NEEDS_ACK }, + { WFC_UPLOAD_PROGRAM, "upload program", 32, 1, 0 }, + { WFC_DOWNLOAD_EDRUM_PROGRAM, "download enhanced drum program", 0, 9, + NEEDS_ACK}, + { WFC_UPLOAD_EDRUM_PROGRAM, "upload enhanced drum program", 8, 1, 0}, + { WFC_SET_EDRUM_CHANNEL, "set enhanced drum program channel", + 0, 1, NEEDS_ACK }, + { WFC_DISABLE_DRUM_PROGRAM, "disable drum program", 0, 1, NEEDS_ACK }, + { WFC_REPORT_CHANNEL_PROGRAMS, "report channel program numbers", + 32, 0, 0 }, + { 0x00 } +}; + +wf_config * +hw_from_dev (int dev) + +{ + int i; + + if ((i = dev2hw[dev]) == -1) { + printk (KERN_ERR + "WaveFront: no hardware associated with device %d.\n", + dev); + return 0; + } + + return &wfs[i]; +} + +static const char * +wavefront_errorstr (int errnum) + +{ + int i; + + for (i = 0; wavefront_errors[i].errstr; i++) { + if (wavefront_errors[i].errno == errnum) { + return wavefront_errors[i].errstr; + } + } + + return "Unknown WaveFront error"; +} + +static wavefront_command * +wavefront_get_command (int cmd) + +{ + int i; + + for (i = 0; wavefront_commands[i].cmd != 0; i++) { + if (cmd == wavefront_commands[i].cmd) { + return &wavefront_commands[i]; + } + } + + return (wavefront_command *) 0; +} + +static int +wavefront_sleep (wf_config *hw, int limit) + +{ + current->timeout = jiffies + limit; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; + + return signal_pending(current); +} + +static int +wavefront_wait (wf_config *hw, int mask) + +{ + int i; + + for (i = 0; i < wf_short_wait_count; i++) { + if (wavefront_status(hw) & mask) { + return 1; + } + } + + for (i = 0; i < wf_sleep_tries; i++) { + + if (wavefront_status(hw) & mask) { + return 1; + } + + if (wavefront_sleep (hw, HZ/wf_sleep_interval)) { + return 0; + } + } + + return 0; +} + +static int +wavefront_read (wf_config *hw) +{ + if (wavefront_wait (hw, STAT_CAN_READ)) + return inb (hw->data_port); + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: read timeout.\n"); + } + return -1; +} + +static int +wavefront_write (wf_config *hw, unsigned char data) +{ + if (wavefront_wait (hw, STAT_CAN_WRITE)) { + outb (data, hw->data_port); + return 1; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: write timeout.\n"); + } + return 0; +} + +static int +wavefront_cmd (wf_config *hw, int cmd, + unsigned char *rbuf, + unsigned char *wbuf) + +{ + int ack; + int i; + int c; + wavefront_command *wfcmd; + + if ((wfcmd = wavefront_get_command (cmd)) == (wavefront_command *) 0) { + printk (KERN_WARNING "WaveFront: command 0x%x not supported.\n", + cmd); + return 1; + } + + /* Hack to handle the one variable-size write command. See + wavefront_send_multisample() for the other half of this + gross and ugly strategy. + */ + + if (cmd == WFC_DOWNLOAD_MULTISAMPLE) { + wfcmd->write_cnt = (unsigned int) rbuf; + rbuf = 0; + } + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "Wavefront: 0x%x [%s] (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt, + wfcmd->need_ack); + } + + if (!wavefront_write (hw, cmd)) { + if (hw->debug & (WF_DEBUG_IO|WF_DEBUG_CMD)) { + printk (KERN_DEBUG "WaveFront: cannot request " + "0x%x [%s].\n", + cmd, wfcmd->action); + } + return 1; + } + + if (wfcmd->write_cnt > 0) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: writing %d bytes " + "for 0x%x\n", + wfcmd->write_cnt, cmd); + } + + for (i = 0; i < wfcmd->write_cnt; i++) { + if (!wavefront_write (hw, wbuf[i])) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad write for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: write[%d] = 0x%x\n", + i, wbuf[i]); + } + } + } + + if (wfcmd->read_cnt > 0) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: reading %d ints " + "for 0x%x\n", + wfcmd->read_cnt, cmd); + } + + for (i = 0; i < wfcmd->read_cnt; i++) { + + if ((c = wavefront_read(hw)) == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + + /* Now handle errors. Lots of special cases here */ + + if (c == 0xff) { + if ((c = wavefront_read (hw)) == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: bad read for error byte at " + "read byte %d of 0x%x [%s].\n", + i, cmd, wfcmd->action); + } + return 1; + } + + /* Can you believe this madness ? */ + + if (c == 1 && + wfcmd->cmd == WFC_IDENTIFY_SAMPLE_TYPE) { + rbuf[0] = WF_ST_EMPTY; + return 0; + + } else if (c == 3 && + wfcmd->cmd == WFC_UPLOAD_PATCH) { + + return 3; + + } else if (c == 1 && + wfcmd->cmd == WFC_UPLOAD_PROGRAM) { + + return 1; + + } else { + + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: error %d (%s) during " + "read for byte " + "%d of 0x%x [%s].\n", + c, + wavefront_errorstr (c), + i, cmd, wfcmd->action); + } + return 1; + + } + } else { + rbuf[i] = c; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: read[%d] = 0x%x\n", + i, rbuf[i]); + } + } + } + + if ((wfcmd->read_cnt == 0 && wfcmd->write_cnt == 0) || wfcmd->need_ack) { + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: reading ACK for 0x%x\n", + cmd); + } + + /* Some commands need an ACK, but return zero instead + of the standard value. + */ + + if ((ack = wavefront_read(hw)) == 0) { + ack = WF_ACK; + } + + if (ack != WF_ACK) { + if (ack == -1) { + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: cannot read ack for 0x%x [%s].\n", + cmd, wfcmd->action); + } + return 1; + + } else { + int err = -1; /* something unknown */ + + if (ack == 0xff) { /* explicit error */ + + if ((err = wavefront_read (hw)) == -1) { + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG + "WaveFront: cannot read err for 0x%x [%s].\n", + cmd, wfcmd->action); + } + } + } + + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG + "WaveFront: 0x%x [%s] " + "failed (0x%x, 0x%x, %s)\n", + cmd, wfcmd->action, ack, err, + wavefront_errorstr (err)); + } + return -err; + } + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: ack received " + "for 0x%x [%s]\n", + cmd, wfcmd->action); + } + } else { + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG + "Wavefront: 0x%x [%s] does not need " + "ACK (%d,%d,%d)\n", + cmd, wfcmd->action, wfcmd->read_cnt, + wfcmd->write_cnt, wfcmd->need_ack); + } + } + + return 0; + +} + +/*********************************************************************** +WaveFront: data munging + +Things here are weird. All data written to the board cannot +have its most significant bit set. Any data item with values +potentially > 0x7F (127) must be split across multiple bytes. + +Sometimes, we need to munge numeric values that are represented on +the x86 side as 8- to 32-bit values. Sometimes, we need to munge data +that is represented on the x86 side as an array of bytes. The most +efficient approach to handling both cases seems to be to use 2 +different functions for munging and 2 for de-munging. This avoids +weird casting and worrying about bit-level offsets. + +**********************************************************************/ + +static +unsigned char * +munge_int32 (unsigned int src, + unsigned char *dst, + unsigned int dst_size) +{ + int i; + + for (i = 0;i < dst_size; i++) { + *dst = src & 0x7F; /* Mask high bit of LSB */ + src = src >> 7; /* Rotate Right 7 bits */ + /* Note: we leave the upper bits in place */ + + dst++; + }; + return dst; +}; + +static int +demunge_int32 (unsigned char* src, int src_size) + +{ + int i; + int outval = 0; + + for (i = src_size - 1; i >= 0; i--) { + outval=(outval<<7)+src[i]; + } + + return outval; +}; + +static +unsigned char * +munge_buf (unsigned char *src, unsigned char *dst, unsigned int dst_size) + +{ + int i; + unsigned int last = dst_size / 2; + + for (i = 0; i < last; i++) { + *dst++ = src[i] & 0x7f; + *dst++ = src[i] >> 7; + } + return dst; +} + +static +unsigned char * +demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes) + +{ + int i; + unsigned char *end = src + src_bytes; + + end = src + src_bytes; + + /* NOTE: src and dst *CAN* point to the same address */ + + for (i = 0; src != end; i++) { + dst[i] = *src++; + dst[i] |= (*src++)<<7; + } + + return dst; +} + +/*********************************************************************** +WaveFront: sample, patch and program management. +***********************************************************************/ + +static int +wavefront_delete_sample (wf_config *hw, int sample_num) + +{ + unsigned char wbuf[2]; + int x; + + wbuf[0] = sample_num & 0x7f; + wbuf[1] = sample_num >> 7; + + if ((x = wavefront_cmd (hw, WFC_DELETE_SAMPLE, 0, wbuf)) == 0) { + hw->sample_status[sample_num] = WF_ST_EMPTY; + } + + return x; +} + +static int +wavefront_get_sample_status (struct wf_config *hw, int assume_rom) + +{ + int i; + unsigned char rbuf[32], wbuf[32]; + unsigned int sc_real, sc_alias, sc_multi; + + /* check sample status */ + + if (wavefront_cmd (hw, WFC_GET_NSAMPLES, rbuf, wbuf)) { + printk ("WaveFront: cannot request sample count.\n"); + } + + sc_real = sc_alias = sc_multi = hw->samples_used = 0; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + + wbuf[0] = i & 0x7f; + wbuf[1] = i >> 7; + + if (wavefront_cmd (hw, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot identify sample " + "type of slot %d\n", i); + hw->sample_status[i] = WF_ST_EMPTY; + continue; + } + + hw->sample_status[i] = (WF_SLOT_FILLED|rbuf[0]); + + if (assume_rom) { + hw->sample_status[i] |= WF_SLOT_ROM; + } + + switch (rbuf[0] & WF_ST_MASK) { + case WF_ST_SAMPLE: + sc_real++; + break; + case WF_ST_MULTISAMPLE: + sc_multi++; + break; + case WF_ST_ALIAS: + sc_alias++; + break; + case WF_ST_EMPTY: + break; + + default: + printk (KERN_WARNING + "WaveFront: unknown sample type for " + "slot %d (0x%x)\n", + i, rbuf[0]); + } + + if (rbuf[0] != WF_ST_EMPTY) { + hw->samples_used++; + } + } + + printk (KERN_INFO + "WaveFront: %d samples used (%d real, %d aliases, %d multi), " + "%d empty\n", hw->samples_used, sc_real, sc_alias, sc_multi, + WF_MAX_SAMPLE - hw->samples_used); + + + return 0; + +} + +static int +wavefront_get_patch_status (struct wf_config *hw) +{ + unsigned char patchbuf[WF_PATCH_BYTES]; + unsigned char patchnum[2]; + wavefront_patch *p; + int i, x, cnt, cnt2; + + for (i = 0; i < WF_MAX_PATCH; i++) { + patchnum[0] = i & 0x7f; + patchnum[1] = i >> 7; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PATCH, patchbuf, + patchnum)) == 0) { + + hw->patch_status[i] |= WF_SLOT_FILLED; + p = (wavefront_patch *) patchbuf; + hw->sample_status + [p->sample_number|(p->sample_msb<<7)] |= + WF_SLOT_USED; + + } else if (x == 3) { /* Bad patch number */ + hw->patch_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload patch " + "error 0x%x\n", x); + hw->patch_status[i] = 0; + } + } + + /* program status has already filled in slot_used bits */ + + for (i = 0, cnt = 0, cnt2 = 0; i < WF_MAX_PATCH; i++) { + if (hw->patch_status[i] & WF_SLOT_FILLED) { + cnt++; + } + if (hw->patch_status[i] & WF_SLOT_USED) { + cnt2++; + } + + } + printk (KERN_INFO + "WaveFront: %d patch slots filled, %d in use\n", cnt, cnt2); + + return 0; +} + +static int +wavefront_get_program_status (struct wf_config *hw) +{ + unsigned char progbuf[WF_PROGRAM_BYTES]; + wavefront_program prog; + unsigned char prognum; + int i, x, l, cnt; + + for (i = 0; i < WF_MAX_PROGRAM; i++) { + prognum = i; + + if ((x = wavefront_cmd (hw, WFC_UPLOAD_PROGRAM, progbuf, + &prognum)) == 0) { + + hw->prog_status[i] |= WF_SLOT_USED; + + demunge_buf (progbuf, (unsigned char *) &prog, + WF_PROGRAM_BYTES); + + for (l = 0; l < WF_NUM_LAYERS; l++) { + if (prog.layer[l].mute) { + hw->patch_status + [prog.layer[l].patch_number] |= + WF_SLOT_USED; + } + } + } else if (x == 1) { /* Bad program number */ + hw->prog_status[i] = 0; + } else { + printk (KERN_ERR "WaveFront: upload program " + "error 0x%x\n", x); + hw->prog_status[i] = 0; + } + } + + for (i = 0, cnt = 0; i < WF_MAX_PROGRAM; i++) { + if (hw->prog_status[i]) { + cnt++; + } + } + + printk (KERN_INFO "WaveFront: %d programs slots in use\n", cnt); + + return 0; +} + +static int +wavefront_send_patch (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PATCH_BYTES+2]; + unsigned char *bptr; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: downloading patch %d\n", + header->number); + } + + hw->patch_status[header->number] |= WF_SLOT_FILLED; + + bptr = buf; + bptr = munge_int32 (header->number, buf, 2); + munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PATCH, 0, buf)) { + printk (KERN_ERR "WaveFront: download patch failed\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_send_program (wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char buf[WF_PROGRAM_BYTES+1]; + int i; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading program %d\n", header->number); + } + + hw->prog_status[header->number] = WF_SLOT_USED; + + /* XXX need to zero existing SLOT_USED bit for program_status[i] + where `i' is the program that's being (potentially) overwritten. + */ + + for (i = 0; i < WF_NUM_LAYERS; i++) { + if (header->hdr.pr.layer[i].mute) { + hw->patch_status[header->hdr.pr.layer[i].patch_number] |= + WF_SLOT_USED; + + /* XXX need to mark SLOT_USED for sample used by + patch_number, but this means we have to load it. Ick. + */ + } + } + + buf[0] = header->number; + munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_PROGRAM, 0, buf)) { + printk (KERN_WARNING "WaveFront: download patch failed\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_freemem (wf_config *hw) + +{ + char rbuf[8]; + + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0)) { + printk (KERN_WARNING "WaveFront: can't get memory stats.\n"); + return -1; + } else { + return demunge_int32 (rbuf, 4); + } +} + +static int +wavefront_send_sample (wf_config *hw, + wavefront_patch_info *header, + UINT16 *dataptr, + int data_is_unsigned) + +{ + /* samples are downloaded via a 16-bit wide i/o port + (you could think of it as 2 adjacent 8-bit wide ports + but its less efficient that way). therefore, all + the blocksizes and so forth listed in the documentation, + and used conventionally to refer to sample sizes, + which are given in 8-bit units (bytes), need to be + divided by 2. + */ + + UINT16 sample_short; + UINT32 length; + UINT16 *data_end = 0; + unsigned int i; + const int max_blksize = 4096/2; + unsigned int written; + unsigned int blocksize; + int dma_ack; + int blocknum; + unsigned char sample_hdr[WF_SAMPLE_HDR_BYTES]; + unsigned char *shptr; + int skip = 0; + int initial_skip = 0; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: sample %sdownload for slot %d, " + "type %d, %d bytes from 0x%x\n", + header->size ? "" : "header ", + header->number, header->subkey, header->size, + (int) header->dataptr); + } + + if (header->size) { + + /* XXX its a debatable point whether or not RDONLY semantics + on the ROM samples should cover just the sample data or + the sample header. For now, it only covers the sample data, + so anyone is free at all times to rewrite sample headers. + + My reason for this is that we have the sample headers + available in the WFB file for General MIDI, and so these + can always be reset if needed. The sample data, however, + cannot be recovered without a complete reset and firmware + reload of the ICS2115, which is a very expensive operation. + + So, doing things this way allows us to honor the notion of + "RESETSAMPLES" reasonably cheaply. Note however, that this + is done purely at user level: there is no WFB parser in + this driver, and so a complete reset (back to General MIDI, + or theoretically some other configuration) is the + responsibility of the user level library. + + To try to do this in the kernel would be a little crazy: + we'd need 24 * 512 bytes (12K) of kernel space just to + hold copies of the original sample headers; the whole + patch/program/sample header data is about 158K!!! + */ + + if (hw->rom_samples_rdonly) { + if (hw->sample_status[header->number] & WF_SLOT_ROM) { + printk (KERN_ERR "WaveFront: sample slot %d " + "write protected\n", + header->number); + return -EACCES; + } + } + + wavefront_delete_sample (hw, header->number); + } + + if (header->size) { + hw->freemem = wavefront_freemem (hw); + + if (hw->freemem < header->size) { + printk (KERN_ERR + "WaveFront: insufficient memory to " + "load %d byte sample.\n", + header->size); + return -ENOMEM; + } + + } + + skip = WF_GET_CHANNEL(&header->hdr.s); + + if (skip > 0) { + switch (header->hdr.s.SampleResolution) { + case LINEAR_16BIT: + break; + default: + printk (KERN_ERR + "WaveFront: channel selection only possible " + "on 16-bit samples"); + return -EINVAL; + } + } + + switch (skip) { + case 0: + initial_skip = 0; + skip = 1; + break; + case 1: + initial_skip = 0; + skip = 2; + break; + case 2: + initial_skip = 1; + skip = 2; + break; + case 3: + initial_skip = 2; + skip = 3; + break; + case 4: + initial_skip = 3; + skip = 4; + break; + case 5: + initial_skip = 4; + skip = 5; + break; + case 6: + initial_skip = 5; + skip = 6; + break; + } + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: channel selection: %d => " + "initial skip = %d, skip = %d\n", + WF_GET_CHANNEL (&header->hdr.s), initial_skip, skip); + } + + /* Be safe, and zero the "Unused" bits ... */ + + WF_SET_CHANNEL(&header->hdr.s, 0); + + /* adjust size for 16 bit samples by dividing by two. We always + send 16 bits per write, even for 8 bit samples, so the length + is always half the size of the sample data in bytes. + */ + + length = header->size / 2; + + /* the data we're sent has not been munged, and in fact, the + header we have to send isn't just a munged copy either. + so, build the sample header right here. + */ + + shptr = &sample_hdr[0]; + + shptr = munge_int32 (header->number, shptr, 2); + + if (header->size) { + shptr = munge_int32 (length, shptr, 4); + } + + /* Yes, a 4 byte result doesn't contain all of the offset bits, + but the offset only uses 24 bits. + */ + + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopStartOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.loopEndOffset), + shptr, 4); + shptr = munge_int32 (*((UINT32 *) &header->hdr.s.sampleEndOffset), + shptr, 4); + + /* This one is truly weird. What kind of weirdo decided that in + a system dominated by 16- and 32-bit integers, they would use + a 12-bit transfer size ? + */ + + shptr = munge_int32 (header->hdr.s.FrequencyBias, shptr, 3); + + /* Why is this nybblified, when the MSB is *always* zero? + Anyway, we can't take address of bitfield, so make a + good-faith guess at where it starts. + */ + + shptr = munge_int32 (*(&header->hdr.s.FrequencyBias+1), + shptr, 2); + + if (wavefront_cmd (hw, header->size ? + WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER, + 0, sample_hdr)) { + printk (KERN_WARNING "WaveFront: sample %sdownload refused.\n", + header->size ? "" : "header "); + return -EIO; + } + + if (header->size == 0) { + goto sent; /* Sorry. Just had to have one somewhere */ + } + + data_end = dataptr + length; + + /* Do any initial skip over an unused channel's data */ + + dataptr += initial_skip; + + for (written = 0, blocknum = 0; + written < length; written += max_blksize, blocknum++) { + + if ((length - written) > max_blksize) { + blocksize = max_blksize; + } else { + /* round to nearest 16-byte value */ + blocksize = ((length-written+7)&~0x7); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_BLOCK, 0, 0)) { + printk (KERN_WARNING "WaveFront: download block " + "request refused.\n"); + return -EIO; + } + + for (i = 0; i < blocksize; i++) { + + if (dataptr < data_end) { + + get_user (sample_short, dataptr); + dataptr += skip; + + if (data_is_unsigned) { + + if (WF_SAMPLE_IS_8BIT(&header->hdr.s)) { + + /* 8 bit sample + resolution, sign + extend both bytes. + */ + + ((unsigned char*) + &sample_short)[0] += 0x7f; + ((unsigned char*) + &sample_short)[1] += 0x7f; + + } else { + + /* 16 bit sample + resolution, sign + extend the MSB. + */ + + sample_short += 0x7fff; + } + } + + } else { + + /* In padding section of final block: + + Don't fetch unsupplied data from + user space, just continue with + whatever the final value was. + */ + } + + if (i < blocksize - 1) { + outw (sample_short, hw->block_port); + } else { + outw (sample_short, hw->last_block_port); + } + } + + /* Get "DMA page acknowledge" */ + + if ((dma_ack = wavefront_read (hw)) != WF_DMA_ACK) { + if (dma_ack == -1) { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack timeout\n"); + return -EIO; + } else { + printk (KERN_ERR "WaveFront: upload sample " + "DMA ack error 0x%x\n", + dma_ack); + return -EIO; + } + } + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_SAMPLE); + + /* Note, label is here because sending the sample header shouldn't + alter the sample_status info at all. + */ + + sent: + return 0; +} + +static int +wavefront_send_alias (struct wf_config *hw, + wavefront_patch_info *header) + +{ + unsigned char alias_hdr[WF_ALIAS_BYTES]; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download alias, %d is " + "alias for %d\n", + header->number, + header->hdr.a.OriginalSample); + } + + munge_int32 (header->number, &alias_hdr[0], 2); + munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), + &alias_hdr[4], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopStartOffset), + &alias_hdr[8], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.loopEndOffset), + &alias_hdr[12], 4); + munge_int32 (*((unsigned int *)&header->hdr.a.sampleEndOffset), + &alias_hdr[16], 4); + munge_int32 (header->hdr.a.FrequencyBias, &alias_hdr[20], 3); + munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2); + + if (wavefront_cmd (hw, WFC_DOWNLOAD_SAMPLE_ALIAS, 0, alias_hdr)) { + printk (KERN_ERR "WaveFront: download alias failed.\n"); + return -EIO; + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); + + return 0; +} + +static int +wavefront_send_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + int num_samples; + unsigned char msample_hdr[WF_MSAMPLE_BYTES]; + + munge_int32 (header->number, &msample_hdr[0], 2); + + /* You'll recall at this point that the "number of samples" value + in a wavefront_multisample struct is actually the log2 of the + real number of samples. + */ + + num_samples = (1<<(header->hdr.ms.NumberOfSamples&7)); + msample_hdr[2] = (unsigned char) header->hdr.ms.NumberOfSamples; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: multi %d with %d=%d samples\n", + header->number, header->hdr.ms.NumberOfSamples, num_samples); + } + + for (i = 0; i < num_samples; i++) { + if ((hw->debug & (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) == + (WF_DEBUG_LOAD_PATCH|WF_DEBUG_DATA)) { + printk (KERN_DEBUG "WaveFront: sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + munge_int32 (header->hdr.ms.SampleNumber[i], + &msample_hdr[3+(i*2)], 2); + } + + /* Need a hack here to pass in the number of bytes + to be written to the synth. This is ugly, and perhaps + one day, I'll fix it. + */ + + if (wavefront_cmd (hw, WFC_DOWNLOAD_MULTISAMPLE, + (unsigned char *) ((num_samples*2)+3), + msample_hdr)) { + printk (KERN_ERR "WaveFront: download of multisample failed.\n"); + return -EIO; + } + + hw->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); + + return 0; +} + +static int +wavefront_fetch_multisample (struct wf_config *hw, + wavefront_patch_info *header) +{ + int i; + unsigned char log_ns[1]; + unsigned char number[2]; + int num_samples; + + munge_int32 (header->number, number, 2); + + if (wavefront_cmd (hw, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { + printk (KERN_ERR "WaveFront: upload multisample failed.\n"); + return -EIO; + } + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample %d has %d samples\n", + header->number, log_ns[0]); + } + + header->hdr.ms.NumberOfSamples = log_ns[0]; + + /* get the number of samples ... */ + + num_samples = (1 << log_ns[0]); + + for (i = 0; i < num_samples; i++) { + char d[2]; + + if ((d[0] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -EIO; + } + + if ((d[1] = wavefront_read (hw)) == -1) { + printk (KERN_ERR "WaveFront: upload multisample failed " + "during sample loop.\n"); + return -EIO; + } + + header->hdr.ms.SampleNumber[i] = + demunge_int32 ((unsigned char *) d, 2); + + if (hw->debug & WF_DEBUG_DATA) { + printk (KERN_DEBUG "WaveFront: msample " + "sample[%d] = %d\n", + i, header->hdr.ms.SampleNumber[i]); + } + } + + return 0; +} + + +static int +wavefront_send_drum (struct wf_config *hw, wavefront_patch_info *header) + +{ + unsigned char drumbuf[WF_DRUM_BYTES]; + wavefront_drum *drum = &header->hdr.d; + int i; + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG + "WaveFront: downloading edrum for MIDI " + "note %d, patch = %d\n", + header->number, drum->PatchNumber); + } + + drumbuf[0] = header->number & 0x7f; + + for (i = 0; i < 4; i++) { + munge_int32 (((unsigned char *)drum)[i], &drumbuf[1+(i*2)], 2); + } + + if (wavefront_cmd (hw, WFC_DOWNLOAD_EDRUM_PROGRAM, 0, drumbuf)) { + printk (KERN_ERR "WaveFront: download drum failed.\n"); + return -EIO; + } + + return 0; +} + +static int +wavefront_find_free_sample (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(hw->sample_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free sample slots!\n"); + return -1; +} + +static int +wavefront_find_free_patch (struct wf_config *hw) + +{ + int i; + + for (i = 0; i < WF_MAX_SAMPLE; i++) { + if (!(hw->patch_status[i] & WF_SLOT_FILLED)) { + return i; + } + } + printk (KERN_WARNING "WaveFront: no free patch slots!\n"); + return -1; +} + +static int +log2_2048(int n) + +{ + int tbl[]={0, 0, 2048, 3246, 4096, 4755, 5294, 5749, 6143, + 6492, 6803, 7084, 7342, 7578, 7797, 8001, 8192, + 8371, 8540, 8699, 8851, 8995, 9132, 9264, 9390, + 9510, 9626, 9738, 9845, 9949, 10049, 10146}; + int i; + + /* Returns 2048*log2(n) */ + + /* FIXME: this is like doing integer math + on quantum particles (RuN) */ + + i=0; + while(n>=32*256) { + n>>=8; + i+=2048*8; + } + while(n>=32) { + n>>=1; + i+=2048; + } + i+=tbl[n]; + return(i); +} + +static int +wavefront_load_gus_patch (struct wf_config *hw, + int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info guspatch; + wavefront_patch_info samp, pat, prog; + wavefront_patch *patp; + wavefront_sample *sampp; + wavefront_program *progp; + + int i,base_note; + long sizeof_patch; + + /* Copy in the header of the GUS patch */ + + sizeof_patch = (long) &guspatch.data[0] - (long) &guspatch; + COPY_FROM_USER (&((char *) &guspatch)[offs], + &(addr)[offs], sizeof_patch - offs); + + if ((i = wavefront_find_free_patch (hw)) == -1) { + return -EBUSY; + } + pat.number = i; + pat.subkey = WF_ST_PATCH; + patp = &pat.hdr.p; + + if ((i = wavefront_find_free_sample (hw)) == -1) { + return -EBUSY; + } + samp.number = i; + samp.subkey = WF_ST_SAMPLE; + samp.size = guspatch.len; + sampp = &samp.hdr.s; + + prog.number = guspatch.instr_no; + progp = &prog.hdr.pr; + + /* Setup the patch structure */ + + patp->amplitude_bias=guspatch.volume; + patp->portamento=0; + patp->sample_number= samp.number & 0xff; + patp->sample_msb= samp.number>>8; + patp->pitch_bend= /*12*/ 0; + patp->mono=1; + patp->retrigger=1; + patp->nohold=(guspatch.mode & WAVE_SUSTAIN_ON) ? 0:1; + patp->frequency_bias=0; + patp->restart=0; + patp->reuse=0; + patp->reset_lfo=1; + patp->fm_src2=0; + patp->fm_src1=WF_MOD_MOD_WHEEL; + patp->am_src=WF_MOD_PRESSURE; + patp->am_amount=127; + patp->fc1_mod_amount=0; + patp->fc2_mod_amount=0; + patp->fm_amount1=0; + patp->fm_amount2=0; + patp->envelope1.attack_level=127; + patp->envelope1.decay1_level=127; + patp->envelope1.decay2_level=127; + patp->envelope1.sustain_level=127; + patp->envelope1.release_level=0; + patp->envelope2.attack_velocity=127; + patp->envelope2.attack_level=127; + patp->envelope2.decay1_level=127; + patp->envelope2.decay2_level=127; + patp->envelope2.sustain_level=127; + patp->envelope2.release_level=0; + patp->envelope2.attack_velocity=127; + patp->randomizer=0; + + /* Program for this patch */ + + progp->layer[0].patch_number= pat.number; /* XXX is this right ? */ + progp->layer[0].mute=1; + progp->layer[0].pan_or_mod=1; + progp->layer[0].pan=7; + progp->layer[0].mix_level=127 /* guspatch.volume */; + progp->layer[0].split_type=0; + progp->layer[0].split_point=0; + progp->layer[0].updown=0; + + for (i = 1; i < 4; i++) { + progp->layer[i].mute=0; + } + + /* Sample data */ + + sampp->SampleResolution=((~guspatch.mode & WAVE_16_BITS)<<1); + + for (base_note=0; + note_to_freq (base_note) < guspatch.base_note; + base_note++); + + if ((guspatch.base_note-note_to_freq(base_note)) + >(note_to_freq(base_note)-guspatch.base_note)) + base_note++; + + printk(KERN_DEBUG "ref freq=%d,base note=%d\n", + guspatch.base_freq, + base_note); + + sampp->FrequencyBias = (29550 - log2_2048(guspatch.base_freq) + + base_note*171); + printk(KERN_DEBUG "Freq Bias is %d\n", sampp->FrequencyBias); + sampp->Loop=(guspatch.mode & WAVE_LOOPING) ? 1:0; + sampp->sampleStartOffset.Fraction=0; + sampp->sampleStartOffset.Integer=0; + sampp->loopStartOffset.Fraction=0; + sampp->loopStartOffset.Integer=guspatch.loop_start + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->loopEndOffset.Fraction=0; + sampp->loopEndOffset.Integer=guspatch.loop_end + >>((guspatch.mode&WAVE_16_BITS) ? 1:0); + sampp->sampleEndOffset.Fraction=0; + sampp->sampleEndOffset.Integer=guspatch.len >> (guspatch.mode&1); + sampp->Bidirectional=(guspatch.mode&WAVE_BIDIR_LOOP) ? 1:0; + sampp->Reverse=(guspatch.mode&WAVE_LOOP_BACK) ? 1:0; + + /* Now ship it down */ + + wavefront_send_sample (hw, &samp, + (unsigned short *) &(addr)[sizeof_patch], + (guspatch.mode & WAVE_UNSIGNED) ? 1:0); + wavefront_send_patch (hw, &pat); + wavefront_send_program (hw, &prog); + + /* Now pan as best we can ... use the slave/internal MIDI device + number if it exists (since it talks to the WaveFront), or the + master otherwise. + */ + +#ifdef CONFIG_MIDI + if (hw->mididev > 0) { + midi_synth_controller (hw->mididev, guspatch.instr_no, 10, + ((guspatch.panning << 4) > 127) ? + 127 : (guspatch.panning << 4)); + } +#endif CONFIG_MIDI + + return(0); +} + +int +wavefront_load_patch (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + + struct wf_config *hw; + wavefront_patch_info header; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + if (format == SYSEX_PATCH) { /* Handled by midi_synth.c */ + if (midi_load_patch == NULL) { + printk (KERN_ERR + "WaveFront: SYSEX not loadable: " + "no midi patch loader!\n"); + return -EINVAL; + } + return midi_load_patch (dev, format, addr, + offs, count, pmgr_flag); + + } else if (format == GUS_PATCH) { + return wavefront_load_gus_patch (hw, dev, format, + addr, offs, count, pmgr_flag); + + } else if (format != WAVEFRONT_PATCH) { + printk (KERN_ERR "WaveFront: unknown patch format %d\n", format); + return -EINVAL; + } + + if (count < sizeof (wavefront_patch_info)) { + printk (KERN_ERR "WaveFront: sample header too short\n"); + return -EINVAL; + } + + /* copied in so far: `offs' bytes from `addr'. We shouldn't copy + them in again, and they correspond to header->key and header->devno. + So now, copy the rest of the wavefront_patch_info struct, except + for the 'hdr' field, since this is handled via indirection + through the 'hdrptr' field. + */ + + COPY_FROM_USER (&((char *) &header)[offs], &(addr)[offs], + sizeof(wavefront_patch_info) - + sizeof(wavefront_any) - offs); + + if (hw->debug & WF_DEBUG_LOAD_PATCH) { + printk (KERN_DEBUG "WaveFront: download " + "Sample type: %d " + "Sample number: %d " + "Sample size: %d\n", + header.subkey, + header.number, + header.size); + } + + switch (header.subkey) { + case WF_ST_SAMPLE: /* sample or sample_header, based on patch->size */ + + COPY_FROM_USER ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_sample)); + + return wavefront_send_sample (hw, &header, header.dataptr, 0); + + case WF_ST_MULTISAMPLE: + + COPY_FROM_USER ((unsigned char *) &header.hdr.s, + (unsigned char *) header.hdrptr, + sizeof (wavefront_multisample)); + + return wavefront_send_multisample (hw, &header); + + + case WF_ST_ALIAS: + + COPY_FROM_USER ((unsigned char *) &header.hdr.a, + (unsigned char *) header.hdrptr, + sizeof (wavefront_alias)); + + return wavefront_send_alias (hw, &header); + + case WF_ST_DRUM: + COPY_FROM_USER ((unsigned char *) &header.hdr.d, + (unsigned char *) header.hdrptr, + sizeof (wavefront_drum)); + + return wavefront_send_drum (hw, &header); + + case WF_ST_PATCH: + COPY_FROM_USER ((unsigned char *) &header.hdr.p, + (unsigned char *) header.hdrptr, + sizeof (wavefront_patch)); + + return wavefront_send_patch (hw, &header); + + case WF_ST_PROGRAM: + COPY_FROM_USER ((unsigned char *) &header.hdr.pr, + (unsigned char *) header.hdrptr, + sizeof (wavefront_program)); + + return wavefront_send_program (hw, &header); + + default: + printk (KERN_ERR "WaveFront: unknown patch type %d.\n", + header.subkey); + return -EINVAL; + } + + return 0; +} + +/*********************************************************************** +WaveFront: /dev/sequencer{,2} and other hardware-dependent interfaces +***********************************************************************/ + +static void +process_sample_hdr (UCHAR8 *buf) + +{ + wavefront_sample s; + UCHAR8 *ptr; + + ptr = buf; + + /* The board doesn't send us an exact copy of a "wavefront_sample" + in response to an Upload Sample Header command. Instead, we + have to convert the data format back into our data structure, + just as in the Download Sample command, where we have to do + something very similar in the reverse direction. + */ + + *((UINT32 *) &s.sampleStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopStartOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.loopEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.sampleEndOffset) = demunge_int32 (ptr, 4); ptr += 4; + *((UINT32 *) &s.FrequencyBias) = demunge_int32 (ptr, 3); ptr += 3; + + s.SampleResolution = *ptr & 0x3; + s.Loop = *ptr & 0x8; + s.Bidirectional = *ptr & 0x10; + s.Reverse = *ptr & 0x40; + + /* Now copy it back to where it came from */ + + memcpy (buf, (unsigned char *) &s, sizeof (wavefront_sample)); +} + +static int +wavefront_synth_control (int dev, int cmd, caddr_t arg) + +{ + struct wf_config *hw; + wavefront_control wc; + unsigned char patchnumbuf[2]; + int i; + + if ((hw = hw_from_dev (dev)) == 0) { + printk (KERN_ERR + "WaveFront: synth_control with unknown " + "device number %d\n", dev); + return -EINVAL; + } + + COPY_FROM_USER (&wc, arg, sizeof (wc)); + + if (hw->debug & WF_DEBUG_CMD) { + printk (KERN_DEBUG "WaveFront: synth control with " + "cmd 0x%x\n", wc.cmd); + } + + /* special case handling of or for various commands */ + + switch (wc.cmd) { + case WFC_DISABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts disabled.\n"); + outb (0x80|0x20, hw->control_port); + hw->interrupts_on = 0; + return 0; + + case WFC_ENABLE_INTERRUPTS: + printk (KERN_INFO "WaveFront: interrupts enabled.\n"); + outb (0x80|0x20|0x40, hw->control_port); + hw->interrupts_on = 1; + return 0; + + case WFC_INTERRUPT_STATUS: + wc.rbuf[0] = hw->interrupts_on; + return 0; + + case WFC_ROMSAMPLES_RDONLY: + hw->rom_samples_rdonly = wc.wbuf[0]; + wc.status = 0; + return 0; + + case WFC_IDENTIFY_SLOT_TYPE: + i = wc.wbuf[0] | (wc.wbuf[1] << 7); + if (i <0 || i >= WF_MAX_SAMPLE) { + printk (KERN_WARNING "WaveFront: invalid slot ID %d\n", + i); + wc.status = EINVAL; + return 0; + } + wc.rbuf[0] = hw->sample_status[i]; + wc.status = 0; + return 0; + + case WFC_DEBUG_DRIVER: + hw->debug = wc.wbuf[0]; + printk (KERN_INFO "WaveFront: debug = 0x%x\n", hw->debug); + return 0; + + case WFC_FX_IOCTL: + wffx_ioctl (hw, (wavefront_fx_info *) &wc.wbuf[0]); + return 0; + + case WFC_UPLOAD_PATCH: + munge_int32 (*((UINT32 *) wc.wbuf), patchnumbuf, 2); + memcpy (wc.wbuf, patchnumbuf, 2); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for various uploads " + "being considered.\n"); + wc.status = EINVAL; + return -EINVAL; + } + + wc.status = wavefront_cmd (hw, wc.cmd, wc.rbuf, wc.wbuf); + + /* Special case handling of certain commands. + + In particular, if the command was an upload, demunge the data + so that the user-level doesn't have to think about it. + */ + + if (wc.status == 0) { + switch (wc.cmd) { + /* intercept any freemem requests so that we know + we are always current with the user-level view + of things. + */ + + case WFC_REPORT_FREE_MEMORY: + hw->freemem = demunge_int32 (wc.rbuf, 4); + break; + + case WFC_UPLOAD_PATCH: + demunge_buf (wc.rbuf, wc.rbuf, WF_PATCH_BYTES); + break; + + case WFC_UPLOAD_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_PROGRAM_BYTES); + break; + + case WFC_UPLOAD_EDRUM_PROGRAM: + demunge_buf (wc.rbuf, wc.rbuf, WF_DRUM_BYTES - 1); + break; + + case WFC_UPLOAD_SAMPLE_HEADER: + process_sample_hdr (wc.rbuf); + break; + + case WFC_UPLOAD_MULTISAMPLE: + case WFC_UPLOAD_SAMPLE_ALIAS: + printk (KERN_INFO "WaveFront: support for " + "various uploads " + "being considered.\n"); + break; + + case WFC_VMIDI_OFF: + virtual_midi_disable (hw->mididev); + break; + + case WFC_VMIDI_ON: + virtual_midi_enable (hw->mididev, 0); + break; + + break; + } + } + + /* XXX It would be nice to avoid a complete copy of the whole + struct sometimes. But I think its fast enough that this + is a low priority fix. + */ + + COPY_TO_USER (arg, &wc, sizeof (wc)); + return 0; +} + + +/*********************************************************************** +WaveFront: MIDI synth interface +***********************************************************************/ + + +static int +wavefront_ioctl (int dev, unsigned int cmd, caddr_t arg) +{ + wf_config *hw; + unsigned char rbuf[4]; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + switch (cmd) { + case SNDCTL_SYNTH_INFO: + memcpy (&((char *) arg)[0], &wavefront_info, + sizeof (wavefront_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + printk (KERN_WARNING + "WaveFront: cannot reset sample status in kernel.\n"); + return 0; /* don't force an error */ + break; + + case SNDCTL_SEQ_PERCMODE: + /* XXX does this correspond to anything obvious ?*/ + return 0; /* don't force an error */ + break; + + case SNDCTL_SYNTH_MEMAVL: + if (wavefront_cmd (hw, WFC_REPORT_FREE_MEMORY, rbuf, 0) != 0) { + printk (KERN_ERR + "WaveFront: cannot get free memory size\n"); + return 0; + } else { + hw->freemem = demunge_int32 (rbuf, 4); + return hw->freemem; + } + + case SNDCTL_SYNTH_CONTROL: + return wavefront_synth_control (dev, cmd, arg); + + default: + return -EINVAL; + } +} + +static int +wavefront_open (int dev, int mode) + +{ + struct wf_config *hw; + + if ((hw = hw_from_dev (dev)) == 0) { + return -EINVAL; + } + + if (hw->opened) { + printk (KERN_ERR "WaveFront: warning: device in use\n"); + } + + hw->opened = mode; + return 0; +} + +static void +wavefront_close (int dev) + +{ + struct wf_config *hw; + + if ((hw = hw_from_dev (dev)) == 0) { + printk (KERN_ERR + "WaveFront: close() called on non-existent dev %d", dev); + return; + } + + hw->opened = 0; + hw->debug = 0; + + return; +} + +static void +wavefront_aftertouch (int dev, int channel, int pressure) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_aftertouch (hw->mididev,channel,pressure); +}; + +static void +wavefront_bender (int dev, int chn, int value) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_bender (hw->mididev, chn, value); +}; + +static void +wavefront_controller (int dev, int channel, int ctrl_num, int value) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + if(ctrl_num==CTRL_PITCH_BENDER) wavefront_bender(0,channel,value); + midi_synth_controller (hw->mididev, channel,ctrl_num,value); +}; + +static void +wavefront_panning(int dev, int channel, int pressure) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return; + midi_synth_controller(hw->mididev,channel,CTL_PAN,pressure); +}; + +static int +wavefront_set_instr (int dev, int channel, int instr_no) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + return(midi_synth_set_instr(hw->mididev,channel,instr_no)); +}; + +static int +wavefront_kill_note (int dev, int channel, int note, int volume) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + if (note==255) + return(midi_synth_start_note(hw->mididev, channel, 0, 0)); + return(midi_synth_kill_note(hw->mididev, channel, note, volume)); +}; + +static int +wavefront_start_note (int dev, int channel, int note, int volume) +{ + struct wf_config *hw = hw_from_dev (dev); + if (!hw) return 1; + + if (note==255) { + /*midi_synth_controller(hw->mididev,channel,7,volume);*/ + midi_synth_aftertouch(hw->mididev,channel,volume); + return(0); + }; + if (volume==0) { + volume=127; + midi_synth_aftertouch(hw->mididev,channel,0); + }; + midi_synth_start_note (hw->mididev, channel, note, volume); + return(0); +}; + +static void +wavefront_setup_voice (int dev, int voice, int chn) +{ +}; + +static void wavefront_reset (int dev) + +{ + int i; + for(i=0;i<16;i++) { + midi_synth_kill_note (dev,i,0,0); + }; +}; + +static struct synth_operations wavefront_operations = +{ + "WaveFront", + &wavefront_info, + 0, + SYNTH_TYPE_SAMPLE, + SAMPLE_TYPE_WAVEFRONT, + wavefront_open, + wavefront_close, + wavefront_ioctl, + wavefront_kill_note, + wavefront_start_note, + wavefront_set_instr, + wavefront_reset, + NULL, + wavefront_load_patch, + wavefront_aftertouch, + wavefront_controller, + wavefront_panning, + NULL, + wavefront_bender, + NULL, + wavefront_setup_voice +}; + + +/*********************************************************************** +WaveFront: OSS/Free and/or Linux kernel installation interface +***********************************************************************/ + +void +wavefrontintr (int irq, void *dev_id, struct pt_regs *dummy) +{ + int i; + + if (irq < 0 || irq > 16) { + printk (KERN_WARNING "WaveFront: bogus interrupt %d recv'd\n", + irq); + } else if ((i = irq2hw[irq]) == -1) { + printk (KERN_ALERT + "WaveFront: interrupt from unknown hw (irq=%d).\n", irq); + } else { + wfs[i].irq_ok = 1; + } +} + +/* STATUS REGISTER + +0 Host Rx Interrupt Enable (1=Enabled) +1 Host Rx Register Full (1=Full) +2 Host Rx Interrupt Pending (1=Interrupt) +3 Unused +4 Host Tx Interrupt (1=Enabled) +5 Host Tx Register empty (1=Empty) +6 Host Tx Interrupt Pending (1=Interrupt) +7 Unused + +*/ + +/* 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 +*/ + +int +probe_wavefront (struct address_info *hw_config) + +{ + int i; + int tmp1, tmp2; + unsigned char bits; + unsigned char rbuf[32], wbuf[32]; + wf_config *hw; + + if (hw_config->irq < 0 || hw_config->irq > 16) { + printk (KERN_WARNING "WaveFront: impossible IRQ suggested(%d)\n", + hw_config->irq); + return 0; + } + + /* Yeah yeah, TB docs say 8, but the FX device on the Tropez Plus + takes up another 8 ... + */ + + if (check_region (hw_config->io_base, 16)) { + printk (KERN_ERR "WaveFront: IO address range 0x%x - 0x%x " + "already in use - ignored\n", hw_config->io_base, + hw_config->io_base+15); + return 0; + } + + for (i = 0; i < WAVEFRONT_MAX_DEVICES; i++) { + if (!wfs[i].installed) { + wfs[i].installed = 1; + break; + } + } + + if (i == WAVEFRONT_MAX_DEVICES) { + printk (KERN_WARNING "WaveFront: no device slots available (max = %d).\n", + WAVEFRONT_MAX_DEVICES); + return 0; + } + + hw = &wfs[i]; + + hw->irq = hw_config->irq; + hw->base = hw_config->io_base; + + hw->israw = 0; + hw->debug = wf_debug_default; + hw->interrupts_on = 0; + hw->rom_samples_rdonly = 1; /* XXX default lock on ROM sample slots */ + + hw_config->slots[WF_SYNTH_SLOT] = hw->synthdev = -1; + hw_config->slots[WF_INTERNAL_MIDI_SLOT] = hw->mididev = -1; + hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = hw->ext_mididev = -1; + + irq2hw[hw_config->irq] = i; + + if (wavefront_cmd (hw, WFC_FIRMWARE_VERSION, rbuf, wbuf) == 0) { + hw->fw_version[0] = rbuf[0]; + hw->fw_version[1] = rbuf[1]; + printk (KERN_INFO "WaveFront: firmware %d.%d already loaded.\n", + rbuf[0], rbuf[1]); + + if (wavefront_cmd (hw, WFC_HARDWARE_VERSION, rbuf, wbuf) == 0) { + hw->hw_version[0] = rbuf[0]; + hw->hw_version[1] = rbuf[1]; + } else { + printk (KERN_INFO "WaveFront: not raw, but no hardware version!\n"); + return 0; + } + if (!wf_raw) { + return 1; + } + } else { + hw->israw = 1; + printk (KERN_INFO + "WaveFront: no response to firmware probe, " + "assume raw.\n"); + } + + if (request_irq (hw_config->irq, wavefrontintr, + 0, "WaveFront", NULL) < 0) { + printk (KERN_WARNING "WaveFront: IRQ %d not available!\n", + hw_config->irq); + return 0; + } + + switch (hw_config->irq) { + case 9: + bits = 0x00; + break; + case 5: + bits = 0x08; + break; + case 12: + bits = 0x10; + break; + case 15: + bits = 0x18; + break; + + default: + printk (KERN_WARNING "WaveFront: invalid IRQ %d\n", + hw_config->irq); + return 0; + } + + /* try reset of port */ + + outb (0x0, hw->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 + + XXX PBD: I think this documentation is backwards. I leave bit + 6 unset, and get MIDI data from the 9 pin D connector. + + 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, plus external MIDI interface selected */ + + outb (bits | 0x80, hw->data_port); + + /* take us out of reset, unmute, master + TX + RX interrupts on */ + + outb (0x80|0x20|0x40|0x10|0x1, hw->control_port); + + for (i = 0; i < 1000000 && !hw->irq_ok; i++); + + /* Data port is now the data port, not the h/w initialization port + + The boot ROM will check the OSRAM, and will then + wait for the either the "download OS" or + "report h/w version" commands. + + Any other command will supposedly be ignored. + */ + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: intr not received after h/w un-reset.\n"); + free_irq (hw_config->irq, NULL); + return 0; + } else { + hw->irq_ok = 0; + } + + hw->interrupts_on = 1; + + /* WaveFront SDK says: + + "When the Master Reset is set to zero (0), the audio board is held + in reset, which is the power-up condition. Setting Master Reset to one + (1) allows the on-board processor to run. It takes approximately two + to four seconds, depending on the memory configuration, for the + on-board processor to complete it's initialization routine before it + will respond to commands after a reset." + + Actually, it seems that most of the time, even with 8MB of RAM, + its actually ready immediately. + */ + + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + if (!wavefront_wait (hw, STAT_CAN_WRITE)) { + printk (KERN_WARNING + "WaveFront: OS not ready after " + "memory check.\n"); + free_irq (hw_config->irq, NULL); + return 0; + } + } + } + + /* get H/W version, and check we get an interrupt */ + + outb (WFC_HARDWARE_VERSION, hw->data_port); + + for (i = 0; i < 1000000 && !hw->irq_ok; i++); + + /* We don't need the IRQ anymore for the WaveFront code, + and to allow an MPU-401 driver to attach to it later, lets + give it back .... + + DO NOT alter irq2hw[], since we'll still use this to lookup + the config struct from an address_info struct. + */ + + free_irq (hw_config->irq, NULL); + + if (!hw->irq_ok) { + printk (KERN_WARNING + "WaveFront: interrupt not received after " + "h/w version cmd.\n"); + return 0; + } else { + hw->irq_ok = 0; + } + + if ((tmp1 = wavefront_read(hw)) == -1) { + printk (KERN_WARNING + "WaveFront @ 0x%x not ready, ignoring\n", hw->base); + return 0; + } + + if ((tmp2 = wavefront_read(hw)) == -1) { + printk (KERN_WARNING + "WaveFront @ 0x%x not responding correctly, ignoring\n", + hw->base); + return 0; + } + + printk (KERN_INFO "WaveFront: hardware version %d.%d\n", tmp1, tmp2); + + return 1; +} + +#include "os.h" +#define __KERNEL_SYSCALLS__ +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/unistd.h> +#include <asm/uaccess.h> + +static int errno; + +static int +wavefront_download_firmware (wf_config *hw, char *path) + +{ + unsigned char section[WF_SECTION_MAX]; + 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 = open (path, 0, 0)) < 0) { + printk (KERN_WARNING "WaveFront: Unable to load \"%s\".\n", path); + return 1; + } + + while (1) { + int x; + + if ((x = read (fd, §ion_length, sizeof (section_length))) != + sizeof (section_length)) { + printk (KERN_ERR "WaveFront: firmware read error.\n"); + goto failure; + } + + if (section_length == 0) { + break; + } + + if (read (fd, section, section_length) != section_length) { + printk (KERN_ERR "WaveFront: firmware section " + "read error.\n"); + goto failure; + } + + /* Send command */ + + if (!wavefront_write (hw, WFC_DOWNLOAD_OS)) { + goto failure; + } + + for (i = 0; i < section_length; i++) { + if (!wavefront_write (hw, section[i])) { + goto failure; + } + } + + /* get ACK */ + + if (wavefront_wait (hw, STAT_CAN_READ)) { + + if ((c = inb (hw->data_port)) != WF_ACK) { + + printk (KERN_ERR "WaveFront: download " + "of section #%d not " + "acknowledged, ack = 0x%x\n", + section_cnt_downloaded + 1, c); + goto failure; + + } else if ((hw->debug & WF_DEBUG_IO) && + !(++section_cnt_downloaded % 10)) { + printk (KERN_DEBUG "."); + } + + } else { + printk (KERN_ERR "WaveFront: timed out " + "for download ACK.\n"); + } + + } + + close (fd); + set_fs (fs); + if (hw->debug & WF_DEBUG_IO) { + printk (KERN_DEBUG "\n"); + } + return 0; + + failure: + close (fd); + set_fs (fs); + printk (KERN_ERR "\nWaveFront: firmware download failed!!!\n"); + return 1; +} + +static int +wavefront_config_midi (wf_config *hw, struct address_info *hw_config) + +{ + unsigned char rbuf[4], wbuf[4]; + + if (!probe_wf_mpu (hw_config)) { + printk (KERN_WARNING "WaveFront: could not install " + "MPU-401 device.\n"); + return 1; + } + + /* Attach an modified MPU-401 driver to the master MIDI interface */ + + hw_config->name = "WaveFront Internal MIDI"; + attach_wf_mpu (hw_config); + + if (hw_config->slots[WF_INTERNAL_MIDI_SLOT] == -1) { + printk (KERN_WARNING "WaveFront: MPU-401 not configured.\n"); + return 1; + } + + hw->mididev = hw_config->slots[WF_INTERNAL_MIDI_SLOT]; + + /* Route external MIDI to WaveFront synth (by default) */ + + if (wavefront_cmd (hw, WFC_MISYNTH_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable MIDI-IN to synth routing.\n"); + /* XXX error ? */ + } + + /* Get the regular MIDI patch loading function, so we can + use it if we ever get handed a SYSEX patch. This is + unlikely, because its so damn slow, but we may as well + leave this functionality from maui.c behind, since it + could be useful for sequencer applications that can + only use MIDI to do patch loading. + */ + + if (midi_devs[hw->mididev]->converter != NULL) { + midi_load_patch = midi_devs[hw->mididev]->converter->load_patch; + midi_devs[hw->mididev]->converter->load_patch = + &wavefront_load_patch; + } + + /* Turn on Virtual MIDI, but first *always* turn it off, + since otherwise consectutive 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 (wavefront_cmd (hw, WFC_VMIDI_OFF, rbuf, wbuf)) { + printk (KERN_WARNING "WaveFront: cannot disable " + "virtual MIDI mode\n"); + /* XXX go ahead and try anyway ? */ + } + + hw_config->name = "WaveFront External MIDI"; + + if (virtual_midi_enable (hw->mididev, hw_config)) { + printk (KERN_WARNING "WaveFront: no virtual MIDI access.\n"); + } else { + hw->ext_mididev = hw_config->slots[WF_EXTERNAL_MIDI_SLOT]; + if (wavefront_cmd (hw, WFC_VMIDI_ON, rbuf, wbuf)) { + printk (KERN_WARNING + "WaveFront: cannot enable virtual MIDI mode.\n"); + virtual_midi_disable (hw->mididev); + } + } + + return 0; +} + +static int +wavefront_init (wf_config *hw, struct address_info *hw_config) + +{ + int samples_are_from_rom = 0; + + /* XXX what state does the board need to be in before + I can download the firmware ? I think any state + after it acknowledges a hardware version command + post-booting. + */ + + if (hw->israw || wf_raw) { + samples_are_from_rom = 1; + + if (wavefront_download_firmware (hw, wf_ospath)) { + return 1; + } + + /* enter normal operation: + bit 7: (on) reset 0x80 + bit 6: interrupts enabled 0x40 + bit 5: (on) mute (i.e. in play mode) 0x20 + bits 4-0: zero + + note: tx and rx interrupts turned off + */ + + outb (0x80|0x40|0x20, hw->control_port); + + /* Set up MPU-401 emulation mode. For some reason, this always + fails the first time, and generates no ACK, so we'll + treat it as a special case by sleeping for a while, and then + trying again. + */ + + wavefront_write (hw, 0xf0); + wavefront_write (hw, 1); + wavefront_sleep (hw, (HZ/wf_sleep_interval) * wf_sleep_tries); + wavefront_write (hw, 0xf0); + wavefront_write (hw, 1); + if (wavefront_read (hw) != 0x80) { + printk (KERN_ERR + "WaveFront: set MPU emulation mode " + "command failed.\n"); + return (1); + } + } + + hw->freemem = wavefront_freemem (hw); + printk (KERN_INFO "WaveFront: available DRAM %dk\n", hw->freemem / 1024); + + wavefront_get_sample_status (hw, samples_are_from_rom); + wavefront_get_program_status (hw); + wavefront_get_patch_status (hw); + + if (fx_raw) { + wffx_init (hw); + } + + return 0; +} + +void +attach_wavefront (struct address_info *hw_config) +{ + int i; + struct wf_config *hw; + + if ((i = irq2hw[hw_config->irq]) == -1) { + printk (KERN_ERR "WaveFront: cannot attach unknown irq 0x%x.\n", + hw_config->irq); + return; + } + + hw = &wfs[i]; + + if ((i = sound_alloc_synthdev()) == -1) { + printk (KERN_ERR "WaveFront: Too many synthesizers\n"); + return; + } else { + hw_config->slots[WF_SYNTH_SLOT] = i; + hw->synthdev = i; + dev2hw[hw->synthdev] = i; + synth_devs[hw->synthdev] = &wavefront_operations; + } + + if (wavefront_init (hw, hw_config)) { + printk (KERN_WARNING "WaveFront: board could not " + "be initialized.\n"); + sound_unload_synthdev (i); + hw->installed = 0; + return; + } + + request_region (hw_config->io_base+2, 6, "WaveFront synth"); + request_region (hw_config->io_base+8, 8, "WaveFront FX"); + + conf_printf2 ("WaveFront Synth", hw_config->io_base, 0, -1, -1); + +#if defined(CONFIG_MIDI) + if (wavefront_config_midi (hw, hw_config)) { + printk (KERN_WARNING "WaveFront: could not initialize MIDI.\n"); + } +#else + printk (KERN_WARNING + "WaveFront: MIDI not configured at kernel-config time.\n"); +#endif CONFIG_MIDI + + return; +} +void +unload_wavefront (struct address_info *hw_config) +{ + struct wf_config *hw; + int i; + + if ((i = irq2hw[hw_config->irq]) == -1) { + printk (KERN_ERR "WaveFront: unloading unrecognized device!\n"); + return; + } + hw = &wfs[i]; + + /* the first two are freed by the wf_mpu code */ + release_region (hw_config->io_base+2, 6); + release_region (hw_config->io_base+8, 8); + sound_unload_synthdev (hw->synthdev); +#if defined(CONFIG_MIDI) + unload_wf_mpu (hw_config); +#endif +} + +/***********************************************************************/ +/* WaveFront FX control */ +/***********************************************************************/ + +#include "yss225.h" + +/* Control bits for the Load Control Register + */ + +#define FX_LSB_TRANSFER 0x01 /* transfer after DSP LSB byte written */ +#define FX_MSB_TRANSFER 0x02 /* transfer after DSP MSB byte written */ +#define FX_AUTO_INCR 0x04 /* auto-increment DSP address after transfer */ + +static int +wffx_idle (struct wf_config *hw) + +{ + int i; + unsigned int x = 0x80; + + for (i = 0; i < 1000; i++) { + x = inb (hw->fx_status); + if ((x & 0x80) == 0) { + break; + } + } + + if (x & 0x80) { + printk (KERN_ERR "WaveFront: FX device never idle.\n"); + return 0; + } + + return (1); +} + +static void +wffx_mute (struct wf_config *hw, int onoff) + +{ + if (!wffx_idle(hw)) { + return; + } + + outb (onoff ? 0x02 : 0x00, hw->fx_op); +} + +static int +wffx_memset (struct wf_config *hw, int page, + int addr, int cnt, unsigned short *data) +{ + if (page < 0 || page > 7) { + printk (KERN_ERR "WaveFront: FX memset: " + "page must be >= 0 and <= 7\n"); + return -EINVAL; + } + + if (addr < 0 || addr > 0x7f) { + printk (KERN_ERR "WaveFront: FX memset: " + "addr must be >= 0 and <= 7f\n"); + return -EINVAL; + } + + if (cnt == 1) { + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + outb ((data[0] >> 8), hw->fx_dsp_msb); + outb ((data[0] & 0xff), hw->fx_dsp_lsb); + + printk (KERN_INFO "WaveFront: FX: addr %d:%x set to 0x%x\n", + page, addr, data[0]); + + } else { + int i; + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (page, hw->fx_dsp_page); + outb (addr, hw->fx_dsp_addr); + + for (i = 0; i < cnt; i++) { + outb ((data[i] >> 8), hw->fx_dsp_msb); + outb ((data[i] & 0xff), hw->fx_dsp_lsb); + if (!wffx_idle (hw)) { + break; + } + } + + if (i != cnt) { + printk (KERN_WARNING + "WaveFront: FX memset " + "(0x%x, 0x%x, 0x%x, %d) incomplete\n", + page, addr, (int) data, cnt); + return -EIO; + } + } + + return 0; +} + +static int +wffx_ioctl (struct wf_config *hw, wavefront_fx_info *r) + +{ + unsigned short page_data[256]; + unsigned short *pd; + + switch (r->request) { + case WFFX_MUTE: + wffx_mute (hw, r->data[0]); + return 0; + + case WFFX_MEMSET: + + if (r->data[2] <= 0) { + printk (KERN_ERR "WaveFront: cannot write " + "<= 0 bytes to FX\n"); + return -EINVAL; + } else if (r->data[2] == 1) { + pd = (unsigned short *) &r->data[3]; + } else { + if (r->data[2] > sizeof (page_data)) { + printk (KERN_ERR + "WaveFront: cannot write " + "> 255 bytes to FX\n"); + return -EINVAL; + } + COPY_FROM_USER(page_data, (unsigned char *) r->data[3], + r->data[2]); + pd = page_data; + } + + return wffx_memset (hw, + r->data[0], /* page */ + r->data[1], /* addr */ + r->data[2], /* cnt */ + pd); + + default: + printk (KERN_WARNING + "WaveFront: FX: ioctl %d not yet supported\n", + r->request); + return -EINVAL; + } +} + +/* YSS225 initialization. + + This code was developed using DOSEmu. The Turtle Beach SETUPSND + utility was run with I/O tracing in DOSEmu enabled, and a reconstruction + of the port I/O done, using the Yamaha faxback document as a guide + to add more logic to the code. It's really pretty weird. + + There was an alternative approach of just dumping the whole I/O + sequence as a series of port/value pairs and a simple loop + that output it. However, I hope that eventually I'll get more + control over what this code does, and so I tried to stick with + a somewhat "algorithmic" approach. +*/ + +static int +wffx_init (struct wf_config *hw) + +{ + int i; + int j; + + /* Set all bits for all channels on the MOD unit to zero */ + /* XXX But why do this twice ? */ + + for (j = 0; j < 2; j++) { + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + } + + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_op); /* mute on */ + + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x44, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x42, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x43, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7c, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x7e, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x47, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x4a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* either because of stupidity by TB's programmers, or because it + actually does something, rezero the MOD page. + */ + for (i = 0x10; i <= 0xff; i++) { + + if (!wffx_idle (hw)) { + return (-1); + } + + outb (i, hw->fx_mod_addr); + outb (0x0, hw->fx_mod_data); + } + /* load page zero */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero); i += 2) { + outb (page_zero[i], hw->fx_dsp_msb); + outb (page_zero[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now load page one */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one); i += 2) { + outb (page_one[i], hw->fx_dsp_msb); + outb (page_one[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two); i++) { + outb (page_two[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three); i++) { + outb (page_three[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four); i++) { + outb (page_four[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Load memory area (page six) */ + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + for (i = 0; i < sizeof (page_six); i += 3) { + outb (page_six[i], hw->fx_dsp_addr); + outb (page_six[i+1], hw->fx_dsp_msb); + outb (page_six[i+2], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x00, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven); i += 2) { + outb (page_seven[i], hw->fx_dsp_msb); + outb (page_seven[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Now setup the MOD area. We do this algorithmically in order to + save a little data space. It could be done in the same fashion + as the "pages". + */ + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x02, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xb0; i <= 0xbf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xf0; i <= 0xff; i++) { + outb (i, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x10; i <= 0x1d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x1e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x1f; i <= 0x2d; i++) { + outb (i, hw->fx_mod_addr); + outb (0xff, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x2e, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x2f; i <= 0x3e; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x3f, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x40; i <= 0x4d; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x4e, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0x4f, hw->fx_mod_addr); + outb (0x0e, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + + for (i = 0x50; i <= 0x6b; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x6c, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6d, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6e, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + outb (0x6f, hw->fx_mod_addr); + outb (0x40, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0x70; i <= 0x7f; i++) { + outb (i, hw->fx_mod_addr); + outb (0xc0, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x80; i <= 0xaf; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0xc0; i <= 0xdd; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0xde, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x10, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + + for (i = 0xe0; i <= 0xef; i++) { + outb (i, hw->fx_mod_addr); + outb (0x00, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i <= 0x0f; i++) { + outb (0x01, hw->fx_mod_addr); + outb (i, hw->fx_mod_data); + outb (0x02, hw->fx_mod_addr); + outb (0x01, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x02, hw->fx_op); /* mute on */ + + /* Now set the coefficients and so forth for the programs above */ + + for (i = 0; i < sizeof (coefficients); i += 4) { + outb (coefficients[i], hw->fx_dsp_page); + outb (coefficients[i+1], hw->fx_dsp_addr); + outb (coefficients[i+2], hw->fx_dsp_msb); + outb (coefficients[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + /* Some settings (?) that are too small to bundle into loops */ + + if (!wffx_idle(hw)) return (-1); + outb (0x1e, hw->fx_mod_addr); + outb (0x14, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xde, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + outb (0xdf, hw->fx_mod_addr); + outb (0x20, hw->fx_mod_data); + + /* some more coefficients */ + + if (!wffx_idle(hw)) return (-1); + outb (0x06, hw->fx_dsp_page); + outb (0x78, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x40, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x03, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0b, hw->fx_dsp_addr); + outb (0x0f, hw->fx_dsp_msb); + outb (0xff, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x02, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x0a, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x46, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + outb (0x07, hw->fx_dsp_page); + outb (0x49, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + + /* Now, for some strange reason, lets reload every page + and all the coefficients over again. I have *NO* idea + why this is done. I do know that no sound is produced + is this phase is omitted. + */ + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x00, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_zero_v2); i += 2) { + outb (page_zero_v2[i], hw->fx_dsp_msb); + outb (page_zero_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x01, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_one_v2); i += 2) { + outb (page_one_v2[i], hw->fx_dsp_msb); + outb (page_one_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + if (!wffx_idle(hw)) return (-1); + if (!wffx_idle(hw)) return (-1); + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x02, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_two_v2); i++) { + outb (page_two_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x03, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_three_v2); i++) { + outb (page_three_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x04, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_four_v2); i++) { + outb (page_four_v2[i], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x06, hw->fx_dsp_page); + + /* Page six v.2 is algorithmic */ + + for (i = 0x10; i <= 0x3e; i += 2) { + outb (i, hw->fx_dsp_addr); + outb (0x00, hw->fx_dsp_msb); + outb (0x00, hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (FX_AUTO_INCR|FX_LSB_TRANSFER, hw->fx_lcr); + outb (0x07, hw->fx_dsp_page); + outb (0x10, hw->fx_dsp_addr); + + for (i = 0; i < sizeof (page_seven_v2); i += 2) { + outb (page_seven_v2[i], hw->fx_dsp_msb); + outb (page_seven_v2[i+1], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0x00; i < sizeof(mod_v2); i += 2) { + outb (mod_v2[i], hw->fx_mod_addr); + outb (mod_v2[i+1], hw->fx_mod_data); + if (!wffx_idle(hw)) return (-1); + } + + for (i = 0; i < sizeof (coefficients2); i += 4) { + outb (coefficients2[i], hw->fx_dsp_page); + outb (coefficients2[i+1], hw->fx_dsp_addr); + outb (coefficients2[i+2], hw->fx_dsp_msb); + outb (coefficients2[i+3], hw->fx_dsp_lsb); + if (!wffx_idle(hw)) return (-1); + } + + outb (0x00, hw->fx_op); + if (!wffx_idle(hw)) return (-1); + + for (i = 0; i < sizeof (coefficients3); i += 2) { + int x; + + outb (0x07, hw->fx_dsp_page); + x = (i % 4) ? 0x4e : 0x4c; + outb (x, hw->fx_dsp_addr); + outb (coefficients3[i], hw->fx_dsp_msb); + outb (coefficients3[i+1], hw->fx_dsp_lsb); + } + + outb (0x00, hw->fx_op); /* mute off */ + + return 0; +} + +EXPORT_NO_SYMBOLS; +struct address_info cfg; + +int io = -1; +int irq = -1; + +MODULE_PARM(io,"i"); +MODULE_PARM(irq,"i"); + +int init_module (void) + +{ + printk ("Turtle Beach WaveFront Driver\n" + "Copyright (C) by Hannu Savolainen, " + "Paul Barton-Davis 1993-1998.\n"); + + if (io == -1 || irq == -1) { + printk (KERN_INFO "WaveFront: irq and io " + "options must be set.\n"); + return -EINVAL; + } + cfg.io_base = io; + cfg.irq = irq; + + if (probe_wavefront (&cfg) == 0) { + return -ENODEV; + } + attach_wavefront (&cfg); + SOUND_LOCK; + return 0; +} + +void cleanup_module (void) + +{ + unload_wavefront (&cfg); + SOUND_LOCK_END; +} + +#endif MODULE +#endif CONFIG_SOUND_WAVEFRONT + + |