?? wavfront.c
字號:
/* -*- linux-c -*- * * sound/wavfront.c * * A Linux driver for Turtle Beach WaveFront Series (Maui, Tropez, Tropez Plus) * * 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' DAC/ADC (a Crystal CS4232) is supported by cs4232.[co], * This chip also controls the configuration of the card: the wavefront * synth is logical unit 4. * * * Supported devices: * * /dev/dsp - using cs4232+ad1848 modules, OSS compatible * /dev/midiNN and /dev/midiNN+1 - using wf_midi code, OSS compatible * /dev/synth00 - raw synth interface * ********************************************************************** * * 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 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. * * $Id: wavfront.c,v 0.7 1998/09/09 15:47:36 pbd Exp $ * * 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. * * Changes: * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> * Added some __init and __initdata to entries in yss225.c */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <linux/ptrace.h>#include <linux/fcntl.h>#include <linux/ioport.h> #include <linux/interrupt.h>#include <linux/config.h>#include <linux/delay.h>#include "sound_config.h"#include <linux/wavefront.h>#define _MIDI_SYNTH_C_#define MIDI_SYNTH_NAME "WaveFront MIDI"#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT#include "midi_synth.h"/* Compile-time control of the extent to which OSS is supported. I consider /dev/sequencer to be an anachronism, but given its widespread usage by various Linux MIDI software, it seems worth offering support to it if its not too painful. Instead of using /dev/sequencer, I recommend: for synth programming and patch loading: /dev/synthNN for kernel-synchronized MIDI sequencing: the ALSA sequencer for direct MIDI control: /dev/midiNN I have never tried static compilation into the kernel. The #if's for this are really just notes to myself about what the code is for.*/#define OSS_SUPPORT_SEQ 0x1 /* use of /dev/sequencer */#define OSS_SUPPORT_STATIC_INSTALL 0x2 /* static compilation into kernel */#define OSS_SUPPORT_LEVEL 0x1 /* just /dev/sequencer for now */#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQstatic int (*midi_load_patch) (int devno, int format, const char *addr, int offs, int count, int pmgr_flag) = NULL;#endif /* OSS_SUPPORT_SEQ *//* if WF_DEBUG not defined, no run-time debugging messages will be available via the debug flag setting. Given the current beta state of the driver, this will remain set until a future version.*/#define WF_DEBUG 1#ifdef WF_DEBUG/* Thank goodness for gcc's preprocessor ... */#define DPRINT(cond, format, args...) \ if ((dev.debug & (cond)) == (cond)) { \ printk (KERN_DEBUG LOGNAME format, ## args); \ }#else#define DPRINT(cond, format, args...)#endif#define LOGNAME "WaveFront: "/* bitmasks for WaveFront status port value */#define STAT_RINTR_ENABLED 0x01#define STAT_CAN_READ 0x02#define STAT_INTR_READ 0x04#define STAT_WINTR_ENABLED 0x10#define STAT_CAN_WRITE 0x20#define STAT_INTR_WRITE 0x40/*** Module-accessible parameters ***************************************/int wf_raw; /* 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 debug_default; /* 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 *ospath = "/etc/sound/wavefront.os"; /* where to find a processed version of the WaveFront OS */int wait_polls = 2000; /* This is a number of tries we poll the status register before resorting to sleeping. WaveFront being an ISA card each poll takes about 1.2us. So before going to sleep we wait up to 2.4ms in a loop. */int sleep_length = HZ/100; /* This says how long we're going to sleep between polls. 10ms sounds reasonable for fast response. */int sleep_tries = 50; /* Wait for status 0.5 seconds total. */int reset_time = 2; /* hundreths of a second we wait after a HW reset for the expected interrupt. */int ramcheck_time = 20; /* time in seconds to wait while ROM code checks on-board RAM. */int osrun_time = 10; /* time in seconds we wait for the OS to start running. */MODULE_PARM(wf_raw,"i");MODULE_PARM(fx_raw,"i");MODULE_PARM(debug_default,"i");MODULE_PARM(wait_polls,"i");MODULE_PARM(sleep_length,"i");MODULE_PARM(sleep_tries,"i");MODULE_PARM(ospath,"s");MODULE_PARM(reset_time,"i");MODULE_PARM(ramcheck_time,"i");MODULE_PARM(osrun_time,"i");/***************************************************************************//* Note: because this module doesn't export any symbols, this really isn't a global variable, even if it looks like one. I was quite confused by this when I started writing this as a (newer) module -- pbd.*/struct wf_config { 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 */ volatile int irq_cnt; /* ditto */ int opened; /* flag, holds open(2) mode */ char debug; /* debugging flags */ int freemem; /* installed RAM, in bytes */ int synth_dev; /* devno for "raw" synth */ int mididev; /* devno for internal MIDI */ int ext_mididev; /* devno for external MIDI */ int fx_mididev; /* devno for FX MIDI interface */#if OSS_SUPPORT_LEVEL & OSS_SUPPORT_SEQ int oss_dev; /* devno for OSS sequencer synth */#endif /* OSS_SUPPORT_SEQ */ char fw_version[2]; /* major = [0], minor = [1] */ char hw_version[2]; /* major = [0], minor = [1] */ char israw; /* needs Motorola microcode */ char has_fx; /* has FX processor (Tropez+) */ 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 */ wait_queue_head_t interrupt_sleeper; } dev;static int detect_wffx(void);static int wffx_ioctl (wavefront_fx_info *);static int wffx_init (void);static int wavefront_delete_sample (int sampnum);static int wavefront_find_free_sample (void);/* From wf_midi.c */extern int virtual_midi_enable (void);extern int virtual_midi_disable (void);extern int detect_wf_mpu (int, int);extern int install_wf_mpu (void);extern int uninstall_wf_mpu (void);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 1static 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 }, { WFC_NOOP, "the no-op command", 0, 0, NEEDS_ACK }, { 0x00 }};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 inline intwavefront_status (void) { return inb (dev.status_port);}static intwavefront_wait (int mask){ int i; for (i = 0; i < wait_polls; i++) if (wavefront_status() & mask) return 1; for (i = 0; i < sleep_tries; i++) { if (wavefront_status() & mask) { set_current_state(TASK_RUNNING); return 1; } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(sleep_length); if (signal_pending(current)) break; } set_current_state(TASK_RUNNING); return 0;}static intwavefront_read (void){ if (wavefront_wait (STAT_CAN_READ)) return inb (dev.data_port); DPRINT (WF_DEBUG_DATA, "read timeout.\n"); return -1;}static intwavefront_write (unsigned char data){ if (wavefront_wait (STAT_CAN_WRITE)) { outb (data, dev.data_port); return 0; } DPRINT (WF_DEBUG_DATA, "write timeout.\n"); return -1;}static intwavefront_cmd (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 LOGNAME "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; } DPRINT (WF_DEBUG_CMD, "0x%x [%s] (%d,%d,%d)\n", cmd, wfcmd->action, wfcmd->read_cnt, wfcmd->write_cnt, wfcmd->need_ack); if (wavefront_write (cmd)) { DPRINT ((WF_DEBUG_IO|WF_DEBUG_CMD), "cannot request " "0x%x [%s].\n", cmd, wfcmd->action); return 1; } if (wfcmd->write_cnt > 0) { DPRINT (WF_DEBUG_DATA, "writing %d bytes " "for 0x%x\n", wfcmd->write_cnt, cmd); for (i = 0; i < wfcmd->write_cnt; i++) { if (wavefront_write (wbuf[i])) { DPRINT (WF_DEBUG_IO, "bad write for byte " "%d of 0x%x [%s].\n",
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -