diff -urN linux-2.4.19-clean/CREDITS linux-2.4.19/CREDITS --- linux-2.4.19-clean/CREDITS Sun Aug 4 03:44:28 2002 +++ linux-2.4.19/CREDITS Sun Aug 4 03:52:26 2002 @@ -361,6 +361,8 @@ N: Erik Inge Bolsų E: knan@mo.himolde.no D: Misc kernel hacks +D: Updated PC speaker driver for 2.3 +S: Norway N: Andreas E. Bombe E: andreas.bombe@munich.netsurf.de @@ -2698,6 +2700,15 @@ S: Sunnyvale, California 94088-4132 S: USA +N: Stas Sergeev +E: stssppnn@yahoo.com +W: http://dosemu.sourceforge.net/stas +W: http://www.geocities.com/stssppnn/pcsp.html +D: PC-Speaker driver maintainer +D: vm86 fixes, OSS fixes +D: dosemu hacking +S: Russia + N: Simon Shapiro E: shimon@i-Connect.Net W: http://www.-i-Connect.Net/~shimon diff -urN linux-2.4.19-clean/Documentation/Configure.help linux-2.4.19/Documentation/Configure.help --- linux-2.4.19-clean/Documentation/Configure.help Sun Aug 4 03:44:29 2002 +++ linux-2.4.19/Documentation/Configure.help Fri Aug 16 18:52:24 2002 @@ -18617,11 +18617,6 @@ as ; the module will be called soundcore.o. - I'm told that even without a sound card, you can make your computer - say more than an occasional beep, by programming the PC speaker. - Kernel patches and supporting utilities to do that are in the pcsp - package, available at . - OSS sound modules CONFIG_SOUND_OSS OSS is the Open Sound System suite of sound card drivers. They make @@ -19401,6 +19396,51 @@ Maestro 2E. See for more details. +Internal PC speaker support +CONFIG_PCSP + If you don't have a sound card in your computer, you can include a + driver for the PC speaker which allows it to act like a primitive + sound card. You can compile this as a module, in which case only the + timer-interrupt hooks and frequency initialization code will appear + in the kernel, and the rest of the driver will be loaded as a module + whenever you want. Further this driver supports DACs + (Digital-to-Analog-Converter) connected to the parallel port. + + You don't need this driver if you only want your computer to beep. + +Use doubled modulation frequency +CONFIG_PCSP_HIGH_FRQ + Standard modulation frequency for PC-Speaker is 18.5KHz. + This can produce a high-pitched tone on some motherboards. + If you have this problem, try enabling this option. + It will make PC-Speaker driver use 36KHz if possible, but + it will produce 5-bit sound instead of 6-bit. + + If unsure, say N. + +PC speaker automatic measurement +CONFIG_PCSP_NO_TEST_SPEED + The PCSP-driver automatically tests the speed of your computer and + configure itself at kernel-startup. If your machine is too slow + (386SX and cannot play with more than 12500 Hz) the driver is disabled + and you here a beep. + + However, you can override this if you specify a 'pcsp=SAMPLERATE' at + kernel commandline or by disabling the automatic speed detection. + THIS IS NOT RECOMMENDED, your machine may HANG if you select a + samplerate, which is to high for your machine. + + If the measurement switch the driver off but an older version was + working on your machine, please contact me (beck@dresearch.de). + It is safe to answer 'N' here. + +PC speaker selected samplerate +CONFIG_PCSP_SRATE + If you have disabled the automatically speed test, select here to + real samplerate that is used for PC speaker. The full range from + 12000Hz up to 18356 is allowed. Higher rates results in better + sound quality but may crash your machine if it is too slow (<486DX-33). + ESS Maestro3/Allegro driver CONFIG_SOUND_MAESTRO3 Say Y or M if you have a sound system driven by ESS's Maestro 3 diff -urN linux-2.4.19-clean/Makefile linux-2.4.19/Makefile --- linux-2.4.19-clean/Makefile Sun Aug 4 03:44:29 2002 +++ linux-2.4.19/Makefile Sun Aug 4 03:52:27 2002 @@ -189,6 +189,12 @@ DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o +ifneq ($(CONFIG_SOUND),y) + ifdef CONFIG_PCSP + DRIVERS-y += drivers/sound/sounddrivers.o + endif +endif + DRIVERS := $(DRIVERS-y) diff -urN linux-2.4.19-clean/arch/i386/kernel/time.c linux-2.4.19/arch/i386/kernel/time.c --- linux-2.4.19-clean/arch/i386/kernel/time.c Thu Mar 14 03:37:55 2002 +++ linux-2.4.19/arch/i386/kernel/time.c Fri Aug 16 19:04:44 2002 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -243,6 +244,14 @@ } else jiffies_p = jiffies_t; +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) + /* + * when using PCSP, we must add the accumulated + * clockticks from the PCSP driver + */ + count += pcsp_clockticks - pcsp_timer0_latch; +#endif + count_p = count; count = ((LATCH-1) - count) * TICK_SIZE; diff -urN linux-2.4.19-clean/drivers/char/joystick/analog.c linux-2.4.19/drivers/char/joystick/analog.c --- linux-2.4.19-clean/drivers/char/joystick/analog.c Fri Oct 26 01:01:51 2001 +++ linux-2.4.19/drivers/char/joystick/analog.c Sun Aug 4 03:52:27 2002 @@ -39,6 +39,7 @@ #include #include #include +#include #include MODULE_AUTHOR("Vojtech Pavlik "); @@ -138,7 +139,7 @@ #ifdef __i386__ #define TSC_PRESENT (test_bit(X86_FEATURE_TSC, &boot_cpu_data.x86_capability)) -#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } } while (0) +#define GET_TIME(x) do { if (TSC_PRESENT) rdtscl(x); else { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } x += pcsp_clockticks - pcsp_timer0_latch; } while (0) #define DELTA(x,y) (TSC_PRESENT?((y)-(x)):((x)-(y)+((x)<(y)?1193180L/HZ:0))) #define TIME_NAME (TSC_PRESENT?"TSC":"PIT") #elif __x86_64__ diff -urN linux-2.4.19-clean/drivers/char/joystick/gameport.c linux-2.4.19/drivers/char/joystick/gameport.c --- linux-2.4.19-clean/drivers/char/joystick/gameport.c Sat Sep 15 01:40:00 2001 +++ linux-2.4.19/drivers/char/joystick/gameport.c Sun Aug 4 03:52:27 2002 @@ -39,6 +39,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_LICENSE("GPL"); @@ -64,7 +65,7 @@ { #if defined(__i386__) || defined(__x86_64__) -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; x += pcsp_clockticks - pcsp_timer0_latch; } while (0) #define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) unsigned int i, t, t1, t2, t3, tx; diff -urN linux-2.4.19-clean/drivers/char/vt.c linux-2.4.19/drivers/char/vt.c --- linux-2.4.19-clean/drivers/char/vt.c Fri Nov 16 21:08:28 2001 +++ linux-2.4.19/drivers/char/vt.c Sun Aug 4 03:52:27 2002 @@ -95,9 +95,18 @@ || (defined(__arm__) && defined(CONFIG_HOST_FOOTBRIDGE)) \ || defined(__x86_64__) +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) +extern char pcsp_speaker; +#endif + static void kd_nosound(unsigned long ignored) { +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) + /* can't allow usage of counter 2 if /dev/pcsp use it */ + if (pcsp_speaker) + return; +#endif /* disable counter 2 */ outb(inb_p(0x61)&0xFC, 0x61); return; @@ -110,6 +119,11 @@ unsigned int count = 0; unsigned long flags; +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) + /* can't allow usage of counter 2 if /dev/pcsp use it */ + if (pcsp_speaker) + return; +#endif if (hz > 20 && hz < 32767) count = 1193180 / hz; diff -urN linux-2.4.19-clean/drivers/sound/Config.in linux-2.4.19/drivers/sound/Config.in --- linux-2.4.19-clean/drivers/sound/Config.in Sun Aug 4 03:44:50 2002 +++ linux-2.4.19/drivers/sound/Config.in Fri Aug 16 18:13:46 2002 @@ -126,6 +126,19 @@ bool ' Verbose initialisation' CONFIG_SOUND_TRACEINIT bool ' Persistent DMA buffers' CONFIG_SOUND_DMAP + if [ "$ARCH" = "i386" ]; then + dep_tristate ' Internal PC speaker support' CONFIG_PCSP $CONFIG_SOUND_OSS + fi + if [ "$CONFIG_PCSP" = "y" -o "$CONFIG_PCSP" = "m" ]; then + bool ' Use doubled modulation frequency' CONFIG_PCSP_HIGH_FRQ + fi + if [ "$CONFIG_PCSP" = "y" -o "$CONFIG_PCSP" = "m" ]; then + bool ' Disable automatic speed detection' CONFIG_PCSP_NO_TEST_SPEED + if [ "$CONFIG_PCSP_NO_TEST_SPEED" = "y" ] ; then + int ' Enter a samplerate for PCSP [12000-18356]' CONFIG_PCSP_SRATE 18356 + fi + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' AD1816(A) based cards (EXPERIMENTAL)' CONFIG_SOUND_AD1816 $CONFIG_SOUND_OSS fi diff -urN linux-2.4.19-clean/drivers/sound/Makefile linux-2.4.19/drivers/sound/Makefile --- linux-2.4.19-clean/drivers/sound/Makefile Sun Aug 4 03:44:50 2002 +++ linux-2.4.19/drivers/sound/Makefile Sun Aug 4 03:52:27 2002 @@ -10,7 +10,7 @@ export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ msnd.o opl3.o sb_common.o sequencer_syms.o \ sound_core.o sound_syms.o uart401.o \ - nm256_audio.o ac97.o ac97_codec.o aci.o + nm256_audio.o ac97.o ac97_codec.o aci.o pcsp_stub.o # Each configuration option enables a list of files. @@ -71,6 +71,7 @@ obj-$(CONFIG_SOUND_MAESTRO) += maestro.o obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o +obj-$(CONFIG_PCSP) += pcsnd.o obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o @@ -92,6 +93,10 @@ obj-y += cs4281/cs4281.o endif +ifdef CONFIG_PCSP + obj-y += pcsp_stub.o +endif + subdir-$(CONFIG_DMASOUND) += dmasound ifeq ($(CONFIG_DMASOUND),y) @@ -102,7 +107,7 @@ # Declare multi-part drivers. list-multi := sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \ - soundcore.o wavefront.o + soundcore.o wavefront.o pcsnd.o sound-objs := \ dev_table.o soundcard.o sound_syms.o \ @@ -118,6 +123,7 @@ sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o vidc_mod-objs := vidc.o vidc_fill.o wavefront-objs := wavfront.o wf_midi.o yss225.o +pcsnd-objs := pcsp.o pcsndriv.o pcsp_mixer.o O_TARGET := sounddrivers.o @@ -152,6 +158,9 @@ wavefront.o: $(wavefront-objs) $(LD) -r -o $@ $(wavefront-objs) +pcsnd.o: $(pcsnd-objs) + $(LD) -r -o $@ $(pcsnd-objs) + # Firmware files that need translation # # The translated files are protected by a file that keeps track diff -urN linux-2.4.19-clean/drivers/sound/pcsndriv.c linux-2.4.19/drivers/sound/pcsndriv.c --- linux-2.4.19-clean/drivers/sound/pcsndriv.c Thu Jan 1 03:00:00 1970 +++ linux-2.4.19/drivers/sound/pcsndriv.c Fri Aug 16 19:09:36 2002 @@ -0,0 +1,1274 @@ +/* + * linux/drivers/sound/pcsndriv.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993-1997 Michael Beck + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef MODULE +#include +#define __NO_VERSION__ +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#include +#include "sound_config.h" + +/* to avoid gcc warning we define dsp_ulaw unused since it is really not used */ +#define dsp_ulaw __attribute__((unused)) _unused_junk +#include "ulaw.h" + +#include "pcsp_tables.h" + +#ifdef CONFIG_APM_CPU_IDLE +#include +static void (*saved_pm_idle)(void); +#endif + +/* + * This function will be used to generate a time delay on very fast + * processors; this delay is only needed for the STO1 because it has + * only one latch, so we must wait some time after we write the value + * for the left channel before we can write the right one + * + * as long as I haven't such, it's empty :-) + */ + +//#define PCSP_DEBUG + +#ifndef PCSP_DEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s, %s, %s, line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define PCSP_NUM_BUFFERS (pcsp.frag_cnt ? pcsp.frag_cnt : PCSP_INIT_BUFFERS) +#define PCSP_BUF_SIZE (pcsp.frag_size ? pcsp.frag_size : pcsp.ablk_size) +#define PCSP_NEXT_BUF(i) ((i + 1) % PCSP_NUM_BUFFERS) +#define PCSP_ULAW_XLAT(i) (pcsp.audio_fmt == AFMT_MU_LAW ? ulaw_dsp[i] : i) + +static volatile int pcsp_full = 0; +static volatile int buf_now_written = -1; +static char pcsp_active = 0; +extern int pcsp_sound_dsp; + +#define PCSP_CPU_DELAY + +/* PCSP internal maximum volume, it's hardcoded */ +#define PCSP_MAX_VOLUME 256 + +/* need this */ +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) + +/* the parallel-ports */ +static int pcsp_ports[] = { 0x3bc, 0x378, 0x278 }; +static int pcsp_port_enable[] = { 0, 0, 0 }; + +#define LP_NO 3 +#define LP_B(port) pcsp_ports[port] /* IO address */ +#define LP_S(port) inb_p(LP_B(port) + 1) /* status */ + +/* general pcsp data */ + +extern struct pcsp_status pcsp; + +/* Volume-tables */ + +static unsigned char vl_tab[256]; +static unsigned char left_vol[256], right_vol[256]; + +#ifdef PCSP_MIXER +extern int pcsp_mixer_ioctl(int dev, unsigned int cmd, caddr_t ptr); +#endif + +static wait_queue_head_t pcsp_sleep; + +extern int pcsp_set_irq(int (*func)(void)); +extern int pcsp_release_irq(void); +static void pcsp_stop_timer(void); +void pcsp_close(int dev); + +/* test if a parallel port is free */ + +inline static int pcsp_free_port(int port) +{ + return (pcsp_port_enable[port] || !check_region(LP_B(port), 3)); +} + +static void pcsp_reserve_port(int port) +{ + pcsp_port_enable[port] = 1; + request_region(LP_B(port), 3, "pcsp"); +} + +void pcsp_free_ports(void) +{ + int port; + + for (port = 0; port < LP_NO; ++port) + if (pcsp_port_enable[port]) + release_region(LP_B(port), 3); +} +/* test if a Stereo-on-One is on lp(port) */ +inline static int stereo1_detect(unsigned port) +{ + if (! pcsp_free_port(port)) + return 0; + outb(0x7F, LP_B(port)); + if (LP_S(port) & 0x80) { + outb(0x80, LP_B(port)); + return (LP_S(port) & 0x80) ? 0 : 1; + } + return 0; +} + +/* test if a new Stereo-Circuit is on lp(port) */ +inline static int stereo_nc_detect(unsigned port) +{ + if (! pcsp_free_port(port)) + return 0; + outb(0, LP_B(port)); + if (LP_S(port) & 0x40) { + outb(0xFF, LP_B(port)); + return (LP_S(port) & 0x40) ? 0 : 1; + } + return 0; +} + +/* search for Stereo-on-One, return it's port if found */ +static int stereo1_init(void) +{ + register int i; + + for (i = 0; i < LP_NO; ++i) + if (stereo1_detect(i)) { + pcsp_reserve_port(i); + pcsp.port = LP_B(i); + pcsp.act_dev = SNDCARD_STO1; + return i; + } + return (-ENODEV); +} + +/* search for Stereo-NC, return it's port if found */ +static int stereo_nc_init(void) +{ + register int i; + + for (i = 0; i < LP_NO; ++i) + if (stereo_nc_detect(i)) { + pcsp_reserve_port(i); + pcsp.port = LP_B(i); + pcsp.act_dev = SNDCARD_STNC; + return i; + } + return (-ENODEV); +} + +inline static int pcsp_factor(void) +{ +int factor = 1; +#ifdef PCSP_16BIT + if (pcsp.audio_fmt == AFMT_S16_LE) + factor <<= 1; + if (pcsp.stereo_emu) + factor <<= 1; +#endif + return factor; +} + +static volatile int pcsp_buf_avail(int num) +{ +int i = PCSP_BUF_SIZE - pcsp.in[num]*pcsp_factor(); + + return ((i > 0) ? i : 0); +} + +static void pcsp_reset_buffers(void) +{ +int i; + for(i = 0; i < PCSP_NUM_BUFFERS; i++) + pcsp.in[i] = 0; + pcsp_full = 0; + pcsp.xfer = pcsp.index = 0; + pcsp.cur_buf = pcsp.next_buf = 0; + pcsp.buffer = pcsp.buf[pcsp.cur_buf]; +} + +static void pcsp_next_buffer(void) +{ + pcsp.xfer -= pcsp.in[pcsp.cur_buf] << 16; + assert(pcsp.xfer >= 0); + if (pcsp.xfer < 0) + pcsp.xfer = 0; + pcsp.index = pcsp.xfer >> 16; + pcsp.in[pcsp.cur_buf] = 0; + pcsp.cur_buf = PCSP_NEXT_BUF(pcsp.cur_buf); + pcsp.buffer = pcsp.buf[pcsp.cur_buf]; +} + +static int pcsp_find_free_buffer(void) +{ +int free_buf; + if (pcsp.in[pcsp.next_buf] == 0) + return pcsp.next_buf; + if (pcsp_buf_avail(pcsp.next_buf) <= 0) + return -1; + free_buf = PCSP_NEXT_BUF(pcsp.next_buf); + if (free_buf == pcsp.cur_buf) + return -1; + assert(pcsp.in[free_buf] == 0); + pcsp.in[free_buf] = 0; + return free_buf; +} + +static int pcsp_count_free_buffers(void) +{ +int next_free_buf; + next_free_buf = pcsp_find_free_buffer(); + if (next_free_buf < 0) + return 0; + if (next_free_buf >= pcsp.cur_buf) + return PCSP_NUM_BUFFERS - (next_free_buf - pcsp.cur_buf); + return pcsp.cur_buf - next_free_buf; +} + +static int pcsp_count_used_bytes(void) +{ +int i, cnt, free_buf; + free_buf = pcsp_find_free_buffer(); + if (free_buf == pcsp.cur_buf) + return 0; + if (free_buf < 0) + free_buf = pcsp.cur_buf; + cnt = 0; + i = pcsp.cur_buf; + do { + cnt += pcsp.in[i]; + i = PCSP_NEXT_BUF(i); + } while (i != free_buf); + +#if 0 /* timidity doesn't like it */ + cnt -= pcsp.index; + if (cnt < 0) + cnt = 0; +#endif + + return (cnt * pcsp_factor()); +} + +static int pcsp_prep_buffers(int buf_size, int buf_num) +{ +int i; +//printk("PCSP: prep_buffers called, size=%i num=%i\n",buf_size, buf_num); + if (buf_size * buf_num > PCSP_TOTAL_SIZE || + buf_size > pcsp.ablk_size || buf_size < 4 + || buf_num > PCSP_MAX_BUFFERS || buf_num < 2) + return -EINVAL; + + if (pcsp.timer_on) { + pcsp_stop_timer(); + } + + if (pcsp.allocated) { + vfree(pcsp.data); + pcsp.allocated = 0; + } + if (! (pcsp.data = vmalloc(buf_size * buf_num))) + return -ENOMEM; + pcsp.allocated = 1; + + for (i = 0; i < buf_num; i++) + pcsp.buf[i] = pcsp.data + buf_size * i; + + pcsp_reset_buffers(); + + return 0; +} + +/* the timer-int for playing thru PC-Speaker */ +static int pcsp_do_timer(void) +{ + if (pcsp.cur_buf == buf_now_written) { + assert(0); + pcsp_stop_timer(); + } + else { + if (pcsp.index < pcsp.in[pcsp.cur_buf]) { + outb(pcsp.e, 0x61); + outb(pcsp.e ^ 1, 0x61); + outb(vl_tab[pcsp.buffer[pcsp.index]], 0x42); + + assert(pcsp.si > 0); + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.cur_buf]) { + pcsp_next_buffer(); +#if USE_OSS_FOPS + DMAbuf_outputintr(pcsp_sound_dsp, 1); +#else + if (waitqueue_active(&pcsp_sleep)) { + wake_up_interruptible(&pcsp_sleep); +//printk("PCSP: done block, cur=%i next=%i\n",pcsp.cur_buf,pcsp.next_buf); + pcsp_full = 0; + } +#endif + if (pcsp.in[pcsp.cur_buf] == 0) + pcsp_stop_timer(); + } + } + +#if !USE_OSS_FOPS + /* workaround for missing locking */ + if (waitqueue_active(&pcsp_sleep) && pcsp_full + && (pcsp.in[pcsp.next_buf] == 0)) { +#if 0 +#ifdef PCSP_DEBUG + printk("PCSP: Force wake_up!\n"); +#endif +#endif + wake_up_interruptible(&pcsp_sleep); + pcsp_full = 0; + } +#endif + pcsp_clockticks -= pcsp_timer0_latch; + if (pcsp_clockticks < 0) { + pcsp_clockticks += LATCH; + return 0; + } + return 1; +} + +/* timer-int for playing thru STO1 */ +static int pcsp_do_sto1_timer(void) +{ + static int ret; + + if (pcsp.index < pcsp.in[pcsp.cur_buf]) { + if (pcsp.mode) { + outb(right_vol[pcsp.buffer[pcsp.index++]], pcsp.port); + outb(1, pcsp.port + 2); + outb(0, pcsp.port + 2); + + /* + * I move the following code because + * I need some time delay for the left DAC + * on my 486DX2 + * this will hopefully enough or we need some + * really time wasting jumps here + * + * This time delay will really be a problem for + * pentiums :-( + * + */ + + pcsp_clockticks -= pcsp_timer0_latch; + if (pcsp_clockticks < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + + PCSP_CPU_DELAY; + + outb(left_vol[pcsp.buffer[pcsp.index++]], pcsp.port); + outb(2, pcsp.port + 2); + outb(0, pcsp.port + 2); + } + else { /* Mono */ + outb(left_vol[pcsp.buffer[pcsp.index++]], pcsp.port); + pcsp_clockticks -= pcsp_timer0_latch; + if (pcsp_clockticks < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + } + } + if (pcsp.index >= pcsp.in[pcsp.cur_buf]) { + pcsp_next_buffer(); +#if !USE_OSS_FOPS + if (waitqueue_active(&pcsp_sleep)) + wake_up_interruptible(&pcsp_sleep); +#endif + if (pcsp.in[pcsp.cur_buf] == 0) + pcsp_stop_timer(); + } + return (ret); +} + +/* timer-int for playing thru DACs */ +static int pcsp_do_dac_timer(void) +{ + if (pcsp.index < pcsp.in[pcsp.cur_buf]) { + if (pcsp.act_dev == SNDCARD_DACS) { + if (pcsp.mode) { + outb(left_vol[pcsp.buffer[pcsp.index++]], pcsp.port); + outb(right_vol[pcsp.buffer[pcsp.index++]], pcsp.portS); + } + else { + outb(left_vol[pcsp.buffer[pcsp.index]], pcsp.port); + outb(left_vol[pcsp.buffer[pcsp.index++]], pcsp.portS); + } + } + else /* Simple DAC */ + outb(left_vol[pcsp.buffer[pcsp.index++]], pcsp.port); + } + if (pcsp.index >= pcsp.in[pcsp.cur_buf]) { + pcsp_next_buffer(); +#if !USE_OSS_FOPS + if (waitqueue_active(&pcsp_sleep)) + wake_up_interruptible(&pcsp_sleep); +#endif + if (pcsp.in[pcsp.cur_buf] == 0) + pcsp_stop_timer(); + } + + pcsp_clockticks -= pcsp_timer0_latch; + if (pcsp_clockticks < 0) { + pcsp_clockticks += LATCH; + return 0; + } + return 1; +} + +/* calculate all needed time-consts, return the 'adjusted' samplerate */ +static unsigned pcsp_calc_srate(unsigned rate) +{ + pcsp.timerC = (CLOCK_TICK_RATE + rate / 2) / rate; + pcsp.srate = (CLOCK_TICK_RATE + pcsp.timerC / 2) / pcsp.timerC; + + /* and now for the PC-Speaker */ + pcsp.si = (pcsp.srate << 16) / SRATE; + return pcsp.srate; +} + +static void pcsp_start_timer(void) +{ + int result; + + if (! pcsp.allocated) + return; + + if (pcsp.timer_on || !(pcsp.enable_bits & PCM_ENABLE_OUTPUT)) { + return; + } + + if (! pcsp.in[pcsp.cur_buf]) { + pcsp_reset_buffers(); + return; + } + + pcsp.buffer = pcsp.buf[pcsp.cur_buf]; + + if (pcsp.act_dev == SNDCARD_PCSP) { + pcsp_speaker = 1; + pcsp.e = inb(0x61) | 0x03; + outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(pcsp.timerCF & 0xFF, 0x40); + outb(pcsp.timerCF >> 8 , 0x40); + if (pcsp_set_irq(pcsp_do_timer) < 0) + panic("PCSP: could not modify timer IRQ!"); + pcsp_timer0_latch = pcsp.timerCF; + } + else { /* it's a DAC */ + if (pcsp.act_dev == SNDCARD_STO1) + outb(3,pcsp.port + 2); + + /* get the timer */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(pcsp.timerC & 0xFF, 0x40); + outb(pcsp.timerC >> 8 , 0x40); + if (pcsp.act_dev == SNDCARD_STO1) + result = pcsp_set_irq(pcsp_do_sto1_timer); + else + result = pcsp_set_irq(pcsp_do_dac_timer); + if (result < 0) + panic("PCSP: could not modify timer IRQ!"); + pcsp_timer0_latch = pcsp.timerC; + } + pcsp_clockticks = pcsp.last_clocks; + pcsp.timer_on = 1; + +#ifdef CONFIG_APM_CPU_IDLE + saved_pm_idle = pm_idle; + pm_idle = NULL; +#endif +} + +/* reset the timer to 100 Hz and reset old timer-int */ +static void pcsp_stop_timer(void) +{ +//printk("PCSP: stop timer\n"); + if (pcsp.timer_on) { + /* restore the timer */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + + /* clear clock tick counter */ + pcsp.last_clocks = pcsp_clockticks; + pcsp_timer0_latch = pcsp_clockticks = LATCH; + + if (pcsp_release_irq() < 0) + panic("PCSP: could not reset timer IRQ!"); + + pcsp.timer_on = 0; + } + + if (pcsp_active && buf_now_written != -1) + return; + + /* reset the buffer */ + pcsp_reset_buffers(); + + pcsp_speaker = 0; + + if (!pcsp_active) /* NONBLOCK close() */ + pcsp_close(0); +#ifdef CONFIG_APM_CPU_IDLE + pm_idle = saved_pm_idle; +#endif +} + +/* + calculate a translation-table for PC-Speaker +*/ +static void pcsp_calc_vol(int volume) +{ +short int i, j; + for (i = 0; i < 256; ++i) { + j = (((PCSP_ULAW_XLAT(i) - 128) * volume) / PCSP_MAX_VOLUME) + 128; + vl_tab[i] = sp_tabs[pcsp.gain][j]; + } +} + +/* calculate linear translation table for DACs */ +static void pcsp_calc_voltab(int volume, unsigned char *tab) +{ +short int i; + for (i = 0; i < 256; ++i) + tab[i] = (((PCSP_ULAW_XLAT(i) - 128) * volume) / PCSP_MAX_VOLUME) + 128; +} + +static inline void pcsp_set_voltables(void) +{ + pcsp_calc_voltab(pcsp.left, left_vol); + pcsp_calc_voltab(pcsp.right, right_vol); + pcsp_calc_vol(pcsp.volume); +} + +#ifdef PCSP_MIXER + +/* this is called if /dev/pcmixer change Mastervolume */ +inline void pcsp_set_volume(unsigned short v, unsigned short g) +{ + pcsp.left = (((unsigned)(v & 0x7F) << 8) + 50) / 100; + pcsp.right = (((unsigned)(v & 0x7F00)) + 50) / 100; + pcsp.volume = (pcsp.right + pcsp.left) >> 1; + pcsp.gain = g & 0xff; + pcsp_set_voltables(); +} + +inline unsigned pcsp_get_mode(void) +{ + return pcsp.mode; +} +#endif + +/* + * set the speed for /dev/pcsp, it's now from 4000 - 99225 Hz, + * but DAC's are bounded by the maximal samplerate + */ +inline unsigned long pcsp_set_speed(unsigned long speed) +{ + if (!speed) + return pcsp.srate; + + if (speed < 4000) + speed = 4000; + if (speed > 99225) + speed = 99225; + if (pcsp.act_dev != SNDCARD_PCSP) + if (speed > pcsp.maxrate) + speed = pcsp.maxrate; + return pcsp_calc_srate(speed); +} + +/* + * set the audio type + */ +int pcsp_set_format(int fmt) +{ + if (fmt != AFMT_QUERY) { + if (! (pcsp.fmt_msk & fmt)) /* Not supported */ + fmt = AFMT_U8; /* this is supported */ + pcsp.audio_fmt = fmt; + /* + we must recalculate the volume-tables is we change + ulaw-state + */ + pcsp_set_voltables(); + } + return pcsp.audio_fmt; +} + +/* + * set the stereo mode if possible + */ +static int pcsp_set_stereo(int flag) +{ + if (pcsp.act_dev == SNDCARD_STO1 || + pcsp.act_dev == SNDCARD_DACS ) +#ifdef PCSP_16BIT + pcsp.stereo_emu = 0; + else { + pcsp.stereo_emu = flag ? 1 : 0; + /* test if the emulation is disabled */ + if (! pcsp.enable_emu && pcsp.stereo_emu) { + pcsp.stereo_emu = 0; + pcsp.mode = 0; + return -EINVAL; + } + } + pcsp.mode = flag ? 1 : 0; + pcsp_set_voltables(); +#else + pcsp.mode = flag ? 1 : 0; + else { + pcsp.mode = 0; + if (flag) + return -EINVAL; + } +#endif +//printk("PCSP: stereo set to %i\n",pcsp.mode); + return pcsp.mode; +} + +/* + * wait until the complete buffers are played or a signal has arrised + */ +static void pcsp_sync(void) +{ + while (! signal_pending(current) && pcsp.in[pcsp.cur_buf]) { + if (! pcsp.timer_on) + pcsp_start_timer(); + /* Wait until a complete block are ready */ + interruptible_sleep_on(&pcsp_sleep); + } +} + +/* + * the driver functions + */ +void pcsp_close(int dev) +{ + if (pcsp.allocated) { + vfree(pcsp.data); + pcsp.allocated = 0; + } + + pcsp_active = 0; + MOD_DEC_USE_COUNT; +} + +static int pcsp_release(struct inode * inode, struct file * file) +{ + if (!(file->f_flags & O_NONBLOCK) || pcsp.in[pcsp.cur_buf] == 0) { + pcsp_sync(); + pcsp_stop_timer(); + outb_p(0xb6,0x43); /* binary, mode 2, LSB/MSB, ch 2 */ + pcsp_close(0); + } + else if (!pcsp.timer_on && pcsp.in[pcsp.cur_buf]) + pcsp_start_timer(); + pcsp_active = 0; + return 0; +} + +static int pcsp_open(struct inode * inode, struct file * file) +{ + int ret, minor = MINOR(inode->i_rdev); + + if (pcsp_active) + return -EBUSY; + + if (pcsp.timer_on) + pcsp_stop_timer(); + + switch (minor & 0xf) { + case 3: /* DSP device /dev/dsp* */ +#ifdef PCSP_16BIT + pcsp_set_stereo(1); + if (pcsp_set_format(AFMT_S16_LE) != AFMT_S16_LE) +#endif + pcsp_set_format(AFMT_U8); + pcsp_set_speed(44100); + break; + case 4: /* Sun Audio device /dev/audio* */ + pcsp_set_format(AFMT_MU_LAW); /* input is ULAW */ + pcsp_set_stereo(0); + pcsp_set_speed(PCSP_DEFAULT_RATE); + break; + + default: + printk(KERN_WARNING "PCSP: minor %d (%d) is for unknown device\n",minor,minor& 0xf); + return -ENODEV; + } + + pcsp.timer_on = + pcsp.frag_size = + pcsp.frag_cnt = 0; + pcsp.enable_bits = PCM_ENABLE_OUTPUT; + + if ((ret=pcsp_prep_buffers(ABLK_SIZE, PCSP_INIT_BUFFERS)) == 0) { + pcsp_active = 1; + MOD_INC_USE_COUNT; + } + + return ret; +} + + +/* + * the new version 2 IOCTL's + */ +static int pcsp_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int ret; + int i, *ptr = (int *)arg; + audio_buf_info info; + +#ifdef PCSP_MIXER + if (((cmd >> 8) & 0xff) == 'M') /* it's a Mixer IOCTL */ + return pcsp_mixer_ioctl(0, cmd, (caddr_t)arg); +#endif + switch (cmd) { + case SNDCTL_DSP_SPEED: + if (get_user(arg, ptr)) + return -EFAULT; + arg = pcsp_set_speed(arg); + return put_user(arg, ptr); + + case SOUND_PCM_READ_RATE: + return put_user(pcsp.srate, ptr); + + case SNDCTL_DSP_CHANNELS: /* same as SOUND_PCM_WRITE_CHANNELS */ + if (get_user(arg, ptr)) + return -EFAULT; + if (arg < 1 || arg > 2) + return -EINVAL; + return pcsp_set_stereo(arg - 1); + + case SNDCTL_DSP_STEREO: + if (get_user(arg, ptr)) + return -EFAULT; + return pcsp_set_stereo(arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(pcsp.mode + 1, ptr); + + case SNDCTL_DSP_GETBLKSIZE: + return put_user(PCSP_BUF_SIZE, ptr); + +#if !USE_OSS_FOPS + case SNDCTL_DSP_SYNC: /* syncing, so speed changes work correct */ + pcsp_sync(); + pcsp_stop_timer(); + return (0); +#endif + + case SNDCTL_DSP_RESET: /* stops output immediately */ + pcsp_stop_timer(); + return (0); + + case SNDCTL_DSP_GETFMTS: + return put_user(pcsp.fmt_msk, ptr); + + case SNDCTL_DSP_SETFMT: /* same as SNDCTL_DSP_SAMPLESIZE */ + if (get_user(arg, ptr)) + return -EFAULT; + return put_user(pcsp_set_format(arg), ptr); + + case SNDCTL_DSP_GETOSPACE: + info.fragments = pcsp_count_free_buffers(); + info.fragstotal = PCSP_NUM_BUFFERS; + info.fragsize = PCSP_BUF_SIZE; + info.bytes = info.fragments * info.fragsize + + (pcsp.in[pcsp.next_buf] ? + pcsp_buf_avail(pcsp.next_buf) : 0); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETODELAY: + return put_user(pcsp_count_used_bytes(), ptr); + + case SNDCTL_DSP_GETISPACE: + return (-EINVAL); + + case SNDCTL_DSP_NONBLOCK: +#if !USE_OSS_FOPS + file->f_flags |= O_NONBLOCK; +#endif + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user((DSP_CAP_REVISION & 1) | + DSP_CAP_BATCH/* | DSP_CAP_TRIGGER*/, ptr); + + case SOUND_PCM_READ_BITS: + if (pcsp.audio_fmt == AFMT_S16_LE) + return put_user(16, ptr); + return put_user(8, ptr); + + case SNDCTL_DSP_SUBDIVIDE: + /* Too late to change ? */ + if (get_user(arg, ptr)) + return -EFAULT; + if (pcsp.frag_size || pcsp.frag_cnt) + return -EINVAL; + if (arg != 1 && arg != 2 && arg != 4) + return -EINVAL; + pcsp.frag_size = ABLK_SIZE / arg; + pcsp.frag_cnt = PCSP_INIT_BUFFERS * arg; + return pcsp_prep_buffers(pcsp.frag_size, pcsp.frag_cnt); + + case SNDCTL_DSP_POST: + if (! pcsp.timer_on) + pcsp_start_timer(); + return (0); + + case SNDCTL_DSP_SETFRAGMENT: + { + int bytes, count; + int fact; + + if (get_user(fact, ptr)) + return -EFAULT; + + if (! fact) + return -EIO; + + /* Too late to change ? */ + if (pcsp.frag_size || pcsp.frag_cnt) + return -EINVAL; + + bytes = fact & 0xffff; + count = (fact >> 16) & 0xffff; + + if (bytes < 4 || bytes > 17) /* <16 || > 128k */ + return -EINVAL; + + if (count < 2) + count = 2; + + pcsp.frag_size = bytes ? (1 << bytes) : pcsp.ablk_size; + pcsp.frag_cnt = count; + + if (pcsp.frag_cnt > PCSP_MAX_BUFFERS) + pcsp.frag_cnt = PCSP_MAX_BUFFERS; + if (pcsp.frag_size > pcsp.ablk_size) + pcsp.frag_size = pcsp.ablk_size; + if (pcsp.frag_size * pcsp.frag_cnt > PCSP_TOTAL_SIZE) + pcsp.frag_cnt = PCSP_TOTAL_SIZE / pcsp.frag_size; + + if (pcsp_prep_buffers(pcsp.frag_size, pcsp.frag_cnt)<0) + return -EINVAL; + + return put_user(bytes | (count << 16), ptr); + } + + case PCSP_SET_DEV: + if (get_user(arg, ptr)) + return -EFAULT; + switch(arg) { + case SNDCARD_STO1: + if (stereo1_init() < 0) + return (-ENODEV); + break; + case SNDCARD_PCSP: + case SNDCARD_DACM: + case SNDCARD_DACS: + pcsp.act_dev = arg; break; + case SNDCARD_STNC: + if (stereo_nc_init() < 0) + return (-ENODEV); + break; + default: + return (-ENODEV); + } + /* Perhaps we need to adjust the samplerate */ + pcsp.srate = pcsp_set_speed(pcsp.srate); + return (0); + + case PCSP_GET_DEV: + return put_user(pcsp.act_dev, ptr); + + case PCSP_SET_PORTS: + if (get_user(arg, ptr)) + return -EFAULT; + if ((arg & 0xFF) < LP_NO && (arg >> 8) < LP_NO) { + if (pcsp.act_dev == SNDCARD_STO1) { + if (stereo1_detect(arg & 0xFF)) { + pcsp.port = LP_B(arg & 0xFF); + return (0); + } + } + else if (pcsp.act_dev == SNDCARD_STNC) { + if (stereo_nc_detect(arg & 0xFF)) { + pcsp.port = LP_B(arg & 0xFF); + return (0); + } + } + else { + pcsp.port = LP_B(arg & 0xFF); + pcsp.portS = LP_B((arg >> 8) & 0xFF); + return (0); + } + } + return (-EINVAL); + + case PCSP_GET_PORTS: + ret = 0; + for (i = 0; i < LP_NO; ++i) + if (LP_B(i) == pcsp.port) + ret = i; + for (i = 0; i < LP_NO; ++i) + if (LP_B(i) == pcsp.portS) + ret |= i << 8; + return put_user(ret, ptr); + + case PCSP_GET_MEASURE: + return put_user(pcsp.maxrate, ptr); + + case PCSP_SET_EMU_MODE: +#ifdef PCSP_16BIT + if (get_user(arg, ptr)) + return -EFAULT; + if (arg == PCSP_EMULATION_ON) { + pcsp.enable_emu = 1; + pcsp.fmt_msk |= AFMT_S16_LE; + } + else if (arg == PCSP_EMULATION_OFF) { + pcsp.enable_emu = 0; + pcsp.fmt_msk &= ~AFMT_S16_LE; + } + return put_user(pcsp.enable_emu, ptr); +#endif + case PCSP_GET_VERSION: + return put_user(PCSP_SOUND_VERSION, ptr); + + default : + return (-EINVAL); + } +} + +static ssize_t pcsp_read(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t pcsp_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ +int copy_size, total_bytes_written = 0; +unsigned l, r; +unsigned char *p; +int j; + + if (! pcsp.allocated) + return -EIO; + + assert(pcsp.timer_on || pcsp.cur_buf == pcsp.next_buf); + pcsp_full = 0; + do { + assert(count >= total_bytes_written); + copy_size = pcsp_buf_avail(pcsp.next_buf); /* can't put it under MIN() - racey! */ + copy_size = MIN(count - total_bytes_written, copy_size); + + if (copy_size > 0) { + buf_now_written = pcsp.next_buf; /* this is our lock:) */ + pcsp_full = 0; +#ifdef PCSP_16BIT + p = pcsp.buf[buf_now_written] + pcsp.in[buf_now_written]; + if (pcsp.audio_fmt == AFMT_S16_LE) + if (pcsp.stereo_emu) { + for (j = 0; j < copy_size >> 2; ++j) { + get_user(l, (unsigned char *)&buffer[4*j + 1]); + get_user(r, (unsigned char *)&buffer[4*j + 3]); + l ^= 0x80; + r ^= 0x80; + *p++ = (l + r) >> 1; + } + } + else { + for (j = 0; j < copy_size >> 1; ++j) { + get_user(*p, &buffer[2*j + 1]); + *p++ ^= 0x80; + } + } + else if (pcsp.stereo_emu) { + for (j = 0; j < copy_size >> 1; ++j) { + get_user(l, (unsigned char *)&buffer[2*j + 0]); + get_user(r, (unsigned char *)&buffer[2*j + 1]); + *p++ = (l + r) >> 1; + } + } + else +#endif + copy_from_user(p, buffer, copy_size); + pcsp.in[buf_now_written] += copy_size / pcsp_factor(); + if (pcsp_buf_avail(buf_now_written) <= 0) { + pcsp.next_buf = PCSP_NEXT_BUF(buf_now_written); + assert(pcsp.next_buf == pcsp.cur_buf || + pcsp.in[pcsp.next_buf] == 0); + } + buffer += copy_size; + total_bytes_written += copy_size; + } + else if (count > total_bytes_written) { + buf_now_written = -1; + assert(! pcsp_full); + + if (! pcsp.timer_on) + pcsp_start_timer(); + +#if !USE_OSS_FOPS + if (file->f_flags & O_NONBLOCK) { + return total_bytes_written ? + total_bytes_written : -EAGAIN; + } + pcsp_full = 1; + interruptible_sleep_on(&pcsp_sleep); + if (signal_pending(current)) { + return total_bytes_written ? + total_bytes_written : -EINTR; + } +#endif + } + } while (count > total_bytes_written); + + assert(count == total_bytes_written); + + buf_now_written = -1; +//printk("ret from write(), avail=%i\n",pcsp_buf_avail(pcsp.cur_buf)); + if (! pcsp.timer_on && pcsp_buf_avail(pcsp.cur_buf) <= 0) + pcsp_start_timer(); + + return total_bytes_written; +} + +static unsigned int pcsp_poll(struct file *file, struct poll_table_struct *wait) +{ +unsigned int mask = 0; + poll_wait(file, &pcsp_sleep, wait); + if ((file->f_mode & FMODE_WRITE) && (pcsp_find_free_buffer() != -1)) + mask |= POLLOUT | POLLWRNORM; + if (file->f_mode & FMODE_READ) + mask |= POLLERR; + return mask; +} + +int pcsp_open1(int dev, int mode) +{ +int ret; +//printk("PCSP: open1 called\n"); + if (!(mode & OPEN_WRITE)) + return -EINVAL; + if (pcsp_active) + return -EBUSY; + + pcsp.timer_on = + pcsp.frag_size = + pcsp.frag_cnt = 0; + pcsp.enable_bits = PCM_ENABLE_OUTPUT; + + if ((ret=pcsp_prep_buffers(ABLK_SIZE, PCSP_INIT_BUFFERS)) == 0) { + pcsp_active = 1; + MOD_INC_USE_COUNT; + } + return ret; +} + +void pcsp_halt(int dev) +{ + pcsp_stop_timer(); +} + +int pcsp_speed(int dev, int speed) +{ +//printk("PCSP: speed set to %i\n",speed); + return pcsp_set_speed(speed); +} + +short pcsp_set_channels(int dev, short channels) +{ + if (!channels) + return pcsp.mode + 1; + return pcsp_set_stereo(channels - 1) + 1; +} + +void pcsp_output_block(int dev, unsigned long physbuf, int count, int intrflag) +{ + struct audio_operations *adev = audio_devs[dev]; + struct dma_buffparms *dmap = adev->dmap_out; + char *dma_start = + (char *)(physbuf - (unsigned long)dmap->raw_buf_phys + + (unsigned long)dmap->raw_buf); +//printk("PCSP: output_block called, cur=%i next=%i free=%i\n", +//pcsp.cur_buf,pcsp.next_buf,pcsp_find_free_buffer()); + pcsp_write(NULL, dma_start, count, NULL); +} + +int pcsp_ioctl1(int dev, unsigned int cmd, caddr_t arg) +{ +//printk("PCSP: ioctl called, cmd=%i\n",cmd); + return pcsp_ioctl(NULL, NULL, cmd, (unsigned long)arg); +} + +int pcsp_prepare_output(int dev, int size, int count) +{ +//printk("PCSP: prepare_for_output called, size=%i num=%i rsize=%i rnum=%i\n", +//size, count, +//audio_devs[dev]->dmap_out->fragment_size, audio_devs[dev]->dmap_out->nbufs); + pcsp.frag_size = size; + pcsp.frag_cnt = count; + audio_devs[dev]->dmap_out->flags |= DMA_NODMA; + return pcsp_prep_buffers(size, count); +} + +unsigned int pcsp_set_bits(int dev, unsigned int bits) +{ +//printk("PCSP: set_bits %i called\n", bits); +return pcsp_set_format(bits); +} + +void pcsp_set_triggers(int dev, int bits) +{ +//printk("PCSP: trigger=%i\n",bits); + pcsp.enable_bits = bits; + if (!pcsp.timer_on && (bits & PCM_ENABLE_OUTPUT) && + pcsp.in[pcsp.cur_buf]) + pcsp_start_timer(); +} + +struct file_operations pcsp_dsp_fops = { + owner: THIS_MODULE, + read: pcsp_read, + write: pcsp_write, + poll: pcsp_poll, + ioctl: pcsp_ioctl, + open: pcsp_open, + release: pcsp_release, +}; + +struct audio_driver pcsp_driver = { + owner: THIS_MODULE, + open: pcsp_open1, + close: pcsp_close, + output_block: pcsp_output_block, + ioctl: pcsp_ioctl1, + prepare_for_output: pcsp_prepare_output, + halt_io: pcsp_halt, + trigger: pcsp_set_triggers, + set_speed: pcsp_speed, + set_bits: pcsp_set_bits, + set_channels: pcsp_set_channels, +}; + +int __init pcsp_device_init(void) +{ + int i; + if (! pcsp_enabled) + return 0; + +#if !USE_OSS_FOPS + init_waitqueue_head(&pcsp_sleep); +#endif + /* do we need a first-time initialisation? */ + if (!pcsp.first_boot) + return 0; + + pcsp.first_boot = 0; + pcsp.allocated = 0; + pcsp.xfer = 0; + pcsp_clockticks = pcsp_timer0_latch = pcsp.last_clocks = LATCH; + pcsp_active = 0; + pcsp_speaker = 0; + pcsp.timer_on = 0; + pcsp.mode = 0; + pcsp.audio_fmt = AFMT_U8; + pcsp.fmt_msk = AFMT_QUERY | AFMT_U8 | AFMT_MU_LAW; +#ifdef PCSP_16BIT + pcsp.fmt_msk |= AFMT_S16_LE; +#endif + pcsp_reset_buffers(); + pcsp.act_dev = SNDCARD_PCSP; + pcsp.port = pcsp.portS = 0; + pcsp.left = (PCSP_LEFT_VOL * 256 + 50) / 100; + pcsp.right = (PCSP_RIGHT_VOL * 256 + 50) / 100; + pcsp.volume = (pcsp.left + pcsp.right) >> 1; + pcsp.gain = PCSP_GAIN * PCSP_MAX_GAIN / 100; + pcsp.ablk_size = ABLK_SIZE; + pcsp.frag_size = 0; + pcsp.frag_cnt = 0; + pcsp_set_speed(PCSP_DEFAULT_RATE); + pcsp_calc_vol(pcsp.volume); + pcsp_calc_voltab(PCSP_LEFT_VOL, left_vol); + pcsp_calc_voltab(PCSP_RIGHT_VOL, right_vol); +#ifdef PCSP_16BIT + pcsp.stereo_emu = 0; + pcsp.enable_emu = 1; +#endif +#ifndef MODULE + printk(" PC-Speaker"); +#endif + i = stereo1_init(); + if (i >= 0) { +#ifndef MODULE + printk(", Stereo-on-One at lpt%d", i); +#endif + pcsp.ablk_size = 2 * ABLK_SIZE; + } + i = stereo_nc_init(); + if (i >= 0) { +#ifndef MODULE + printk(", New Stereo Circuit at lpt%d", i); +#endif + pcsp.ablk_size = 2 * ABLK_SIZE; + } + return 0; +} diff -urN linux-2.4.19-clean/drivers/sound/pcsp.c linux-2.4.19/drivers/sound/pcsp.c --- linux-2.4.19-clean/drivers/sound/pcsp.c Thu Jan 1 03:00:00 1970 +++ linux-2.4.19/drivers/sound/pcsp.c Tue Aug 13 00:19:22 2002 @@ -0,0 +1,105 @@ +/* + * linux/drivers/sound/pcsp.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993-1997 Michael Beck + */ + +#include + +#include +#include + +#ifdef MODULE +#include + +MODULE_AUTHOR("Michael Beck "); +MODULE_DESCRIPTION("PC-Speaker Driver for Linux 2.4.x Version 1.4"); +MODULE_SUPPORTED_DEVICE("pcsp"); +EXPORT_NO_SYMBOLS; +#endif +#include +#include "sound_config.h" + +extern struct file_operations pcsp_dsp_fops; +extern struct audio_driver pcsp_driver; +extern struct mixer_operations pcsp_mixer_ops; +extern long pcsp_driver_init(long); +extern int pcsp_set_format(int); +extern void pcsp_free_ports(void); + + +int pcsp_sound_dsp, pcsp_sound_mixer; + +int pcsp_register_driver(void) +{ + if ((pcsp_sound_dsp = sound_install_audiodrv( + AUDIO_DRIVER_VERSION, + "PC-Speaker", + &pcsp_driver, + sizeof(pcsp_driver), + DMA_NODMA, + AFMT_U8 | AFMT_MU_LAW +#ifdef PCSP_16BIT + | AFMT_S16_LE +#endif + ,NULL, -1, -1)) < 0) { + printk(KERN_WARNING "Unable to install PC speaker driver\n"); + return 0; + } +#if !USE_OSS_FOPS + unregister_sound_dsp((pcsp_sound_dsp<<4)+3); + if (register_sound_dsp(&pcsp_dsp_fops, pcsp_sound_dsp) < 0) { + printk(KERN_ERR "Oops, unable to register PC speaker DSP device!\n"); + return 0; + } +#endif +// printk("PCSP on device %d\n",pcsp_sound_dsp); + +#if defined(PCSP_16BIT) || defined(PCSP_MIXER) + if ((pcsp_sound_mixer = sound_install_mixer ( + MIXER_DRIVER_VERSION, + "PC-Speaker", + &pcsp_mixer_ops, + sizeof(pcsp_mixer_ops), + NULL)) < 0) { + printk(KERN_WARNING "Unable to register PC speaker mixer device\n"); + sound_unload_audiodev(pcsp_sound_dsp); + return 0; + } +// printk("PCSP mixer on device %d\n", pcsp_sound_mixer); +#endif + return 1; +} + +#ifdef MODULE + +int __init init_module(void) +{ + if (! pcsp_enabled) { + printk("pcsp disabled\n"); + return -ENODEV; + } + + if (! pcsp_register_driver()) + return -EIO; + + pcsp_device_init(); +#ifdef PCSP_MIXER + pcsp_mixer_init(); +#endif + return 0; +} + +void cleanup_module(void) +{ + if (MOD_IN_USE) + printk("pcsnd: busy - remove delayed\n"); + else { + pcsp_free_ports(); + sound_unload_audiodev(pcsp_sound_dsp); + sound_unload_mixerdev(pcsp_sound_mixer); + } +} +#endif diff -urN linux-2.4.19-clean/drivers/sound/pcsp_mixer.c linux-2.4.19/drivers/sound/pcsp_mixer.c --- linux-2.4.19-clean/drivers/sound/pcsp_mixer.c Thu Jan 1 03:00:00 1970 +++ linux-2.4.19/drivers/sound/pcsp_mixer.c Mon Aug 12 22:29:50 2002 @@ -0,0 +1,143 @@ +/* + * linux/drivers/sound/pcsp_mixer.c + * + * /dev/pcsp implementation - simple Mixer routines + * + * (C) 1993-1997 Michael Beck + * Craig Metz (cmetz@thor.tjhsst.edu) + */ + +#include + +#include + +#ifdef MODULE +#define __NO_VERSION__ +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#include +#include "sound_config.h" +#include "pcsp_tables.h" + +#if defined(PCSP_16BIT) || defined(PCSP_MIXER) + +#define IOCTL_OUT(arg, ret) (put_user(ret, (int *)arg)) +#define OFF 0 + +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_OGAIN) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + PCSP_LEFT_VOL | (PCSP_RIGHT_VOL << 8), /* Master Volume */ + /* The next devices are not supported, so they are zero */ + 0, /* Bass */ + 0, /* Treble */ + 0, /* FM */ + 0, /* PCM */ + 0, /* PC Speaker */ + 0, /* Ext Line */ + 0, /* Mic */ + 0, /* CD */ + 0, /* Recording monitor */ + 0, /* SB PCM */ + 0, /* Recording level */ + 0, /* Input gain */ + PCSP_GAIN | (PCSP_GAIN << 8) /* Output gain */ +}; + +extern void pcsp_set_volume(unsigned short, unsigned); +extern unsigned pcsp_get_mode(void); + +int pcsp_mixer_ioctl(int dev, unsigned int cmd, caddr_t ptr) +{ + int val, left, right; + + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) { + if (get_user(val, (int *)ptr)) + return -EINVAL; + + left = val & 0x7f; + right = (val & 0x7f00) >> 8; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + switch (cmd & 0xff) { + case SOUND_MIXER_VOLUME: /* Master volume (0-127) */ + levels[SOUND_MIXER_VOLUME] = left | (right << 8); + break; + + case SOUND_MIXER_OGAIN: + right = left; + left = (left * PCSP_MAX_GAIN / 100) * 100 / PCSP_MAX_GAIN; + if ((levels[SOUND_MIXER_OGAIN] & 0xff) < right) { + levels[SOUND_MIXER_OGAIN] = left | (left << 8); + if (right > left) + levels[SOUND_MIXER_OGAIN] += + (100 / PCSP_MAX_GAIN) | ((100 / PCSP_MAX_GAIN) << 8); + } + else if ((levels[SOUND_MIXER_OGAIN] & 0xff) > right) { + levels[SOUND_MIXER_OGAIN] = left | (left << 8); + } + break; + + default: + return (-EINVAL); + } + + pcsp_set_volume(levels[SOUND_MIXER_VOLUME], + (levels[SOUND_MIXER_OGAIN] & 0xff) * PCSP_MAX_GAIN / 100); + + return IOCTL_OUT(ptr, val); + } + + switch (cmd & 0xff) { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT(ptr, OFF); + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT(ptr, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_OGAIN)); + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT(ptr, SUPPORTED_MIXER_DEVICES); + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT(ptr, OFF); + + case SOUND_MIXER_CAPS: + return IOCTL_OUT(ptr, 0); + + default: + if ((cmd & 0xff) < SOUND_MIXER_NRDEVICES) + return IOCTL_OUT(ptr, levels[cmd & 0xff]); + else + return (-EINVAL); + } + } + + return (-EINVAL); +} + +struct mixer_operations pcsp_mixer_ops = { + owner: THIS_MODULE, + id: "PC-Speaker", + name: "PCSP Mixer", + ioctl: pcsp_mixer_ioctl +}; + +int pcsp_mixer_init(void) +{ + pcsp_set_volume(levels[SOUND_MIXER_VOLUME], + (levels[SOUND_MIXER_OGAIN] & 0xff) * PCSP_MAX_GAIN / 100); + return 0; +} + +#endif diff -urN linux-2.4.19-clean/drivers/sound/pcsp_stub.c linux-2.4.19/drivers/sound/pcsp_stub.c --- linux-2.4.19-clean/drivers/sound/pcsp_stub.c Thu Jan 1 03:00:00 1970 +++ linux-2.4.19/drivers/sound/pcsp_stub.c Fri Aug 16 19:10:55 2002 @@ -0,0 +1,389 @@ +/* + * linux/drivers/sound/pcsp_stub.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993-1997 Michael Beck + * + * if PCSP is compiled as a module, this part must + * be linked with the kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_PCSP_MODULE +#include +#endif + +#include +#include +#include +#include "sound_config.h" + +/* + * need this macros + */ +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) +#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) + +/* the maximal samplerange for PC-Speaker: 18357 Hz */ +#ifndef CONFIG_PCSP_HIGH_FRQ +#define MIN_CONST 65 +#else +#define MIN_CONST 33 +#endif + +#define MAX_SRATE (CLOCK_TICK_RATE / MIN_CONST) + + +/* + * need this to be global + */ +char pcsp_speaker = 0; +char pcsp_enabled = -1; +volatile int pcsp_timer0_latch, pcsp_clockticks; +volatile int pcsp_test_running; + +struct pcsp_status pcsp; + +static void *sleep = NULL; +static int (*pcsp_IRQ)(void) = NULL; +static struct irqaction pcsp_action, *pcsp_old_action = NULL; + +/* + * this is the PCSP IRQ handler + */ +asmlinkage void pcsp_run_IRQ(int irq, void *dev_id, struct pt_regs *regs) +{ + struct irqaction * action; + int status; + + status = pcsp_IRQ(); + if (! status) { + /* Return with this interrupt masked if no action */ + action = pcsp_old_action; + if (action) { + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + } + } +} + +/* + * Set the function func to be executed as the timer int. + * if func returns a 0, the old IRQ0-handler(s) is called + */ +int pcsp_set_irq(int (*func)(void)) +{ + unsigned long flags; + struct irqaction * action = irq_desc[0].action; + + pcsp_IRQ = func; + if (! pcsp_IRQ || ! action) + return -EINVAL; + + /* fill in the action */ + pcsp_action.handler = pcsp_run_IRQ; + pcsp_action.flags = 0; /* Do NOT allow other IRQ-handlers */ + pcsp_action.mask = 0; + pcsp_action.name = "pcsp+timer"; + pcsp_action.next = NULL; + pcsp_action.dev_id = NULL; + + /* ok, change the handler */ + save_flags(flags); + cli(); + irq_desc[0].action = &pcsp_action; + restore_flags(flags); + pcsp_old_action = action; + return 0; +} + +/* + * reset the IRQ0 to the old handling + */ +int pcsp_release_irq(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + irq_desc[0].action = pcsp_old_action; + restore_flags(flags); + pcsp_IRQ = NULL; + return 0; +} + +#ifndef CONFIG_PCSP_NO_TEST_SPEED +/* + this is a stupid beep which occurs if PCSP is disabled; + it's not needed because we have the message, but who reads it... + and this is the PC-Speaker driver :-) +*/ +void __init pcsp_beep(int count, int cycles) +{ + /* enable counter 2 */ + outb_p(inb_p(0x61)|3, 0x61); + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* select desired HZ */ + outb_p(count & 0xff, 0x42); + outb((count >> 8) & 0xff, 0x42); + + while (cycles--); + + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); +} + +/* + the timer-int for testing cpu-speed, mostly the same as + for PC-Speaker + */ +static int __init pcsp_test_intspeed(void) +{ + if (pcsp.index < pcsp.in[pcsp.cur_buf]) { + outb(pcsp.e, 0x61); + outb(pcsp.e ^ 1, 0x61); + outb(pcsp.buffer[pcsp.index], 0x42); + + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.cur_buf]) { + pcsp.xfer = pcsp.index = 0; + pcsp.in[pcsp.cur_buf] = 0; + pcsp.cur_buf ^= 1; + pcsp.buffer = pcsp.buf[pcsp.cur_buf]; + if (sleep) /* NEVER */ + nop(); + if (pcsp.in[pcsp.cur_buf] == 0xFFFF) + pcsp.cur_buf ^= 1; + } + + ++pcsp_test_running; + return 1; +} + +/* + this routine measures the time needed for one timer-int if + we play thru PC-Speaker. This is kind of ugly but does the + trick. + */ +static int __init pcsp_measurement(unsigned char *buf, int addon) +{ + int count; + unsigned long flags; + + pcsp.timerCF = LATCH; + pcsp.buf[0] = + pcsp.buffer = buf; + pcsp.index = 0; + pcsp.xfer = 0; + pcsp.si = 1 << 16; + pcsp.in[0] = 5 + addon; + pcsp.in[1] = 0; + pcsp.cur_buf = 0; + pcsp.e = inb(0x61) & 0xFC; + + pcsp_test_running = 0; + + if (pcsp_set_irq(pcsp_test_intspeed) < 0) + panic("PCSP could not modify timer IRQ!"); + + /* + Currently (0.99.15d) Linux call chr_dev_init with ints + disabled; so we need a sti() to enable them. + However, because this can be changed in the future we use + save_flags() and restore_flags() + */ + save_flags(flags); + sti(); + + /* + Perhaps we need some sort of timeout here, but if IRQ0 + isn't working the system hangs later ... + */ + while (pcsp_test_running < 5); + restore_flags(flags); + + if (pcsp_release_irq() < 0) + panic("PCSP could not reset timer IRQ!"); + + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + return (LATCH - count); +} + +static int __init pcsp_test_speed(void) +{ + int worst, worst1, best, best1; + unsigned char test_buffer[256]; + + worst = pcsp_measurement(test_buffer, 0); + worst1 = pcsp_measurement(test_buffer, 0); + best = pcsp_measurement(test_buffer, 5); + best1 = pcsp_measurement(test_buffer, 5); + + worst = MAX(worst, worst1); + best = MIN(best, best1); + +#ifdef PCSP_DEBUG + printk(" PCSP-Timerint needs %d Ticks in worst case\n", worst); + printk(" PCSP-Timerint needs %d Ticks in best case\n", best); +#endif + /* We allow a CPU-usage of 90 % for the best-case ! */ + pcsp.timerCF = best * 10 / 9; + pcsp.maxrate = CLOCK_TICK_RATE / pcsp.timerCF; + printk(" maximal samplerate %d Hz", pcsp.maxrate); + + if (pcsp.maxrate > PCSP_CRITICAL_FREQ) { + if (MIN_CONST > pcsp.timerCF) { + pcsp.timerCF = MIN_CONST; + printk(", %d Hz", MAX_SRATE); + } + printk(" used\n"); + return 1; + } + + printk("\n This is too SLOW! PCSP-driver DISABLED\n"); + + /* very ugly beep, but you hopefully never hear it */ + pcsp_beep(12000,800000); + pcsp_beep(10000,800000); + + return 0; +} +#endif + +void __init pcsp_setup(char *s, int *p) +{ + if (!strcmp(s, "off")) { + pcsp_enabled = 0; + return; + } + if (p[0] > 0 && p[1] > 0) + pcsp.maxrate = p[1]; + pcsp_enabled = 1; +} + +/* + * initialise the driver by testing the CPU speed and setting + * the time constants + */ +void __init pcsp_driver_init(void) +{ +int tmp; + if (pcsp_enabled < 0) { +#ifndef CONFIG_PCSP_NO_TEST_SPEED + pcsp_enabled = pcsp_test_speed(); +#else + pcsp.maxrate = 44100; /* only a BIG freq */ + pcsp.timerCF = (CLOCK_TICK_RATE + CONFIG_PCSP_SRATE / 2) / + CONFIG_PCSP_SRATE; + pcsp_enabled = 1; +#endif + } + else { + tmp = MIN(pcsp.maxrate, MAX_SRATE); + pcsp.timerCF = (CLOCK_TICK_RATE + tmp / 2) / tmp; + } +} + +/* + * define the PCSP exports: + * when PCSP is compiled in, only the timer latch must be exported, + * else we need some help for the PCSP module from this stub + */ +EXPORT_SYMBOL(pcsp_timer0_latch); +EXPORT_SYMBOL(pcsp_clockticks); + +#ifdef CONFIG_PCSP_MODULE +EXPORT_SYMBOL(pcsp_enabled); +EXPORT_SYMBOL(pcsp_speaker); +EXPORT_SYMBOL(pcsp); +EXPORT_SYMBOL(pcsp_set_irq); +EXPORT_SYMBOL(pcsp_release_irq); +#endif + +/* + * the pcsp_init() function is called in chrdev_init at kernel startup + */ +#ifndef CONFIG_PCSP_MODULE +int __init pcsp_init(void) +{ + extern int pcsp_sound_dsp; +#if defined(PCSP_16BIT) || defined(PCSP_MIXER) + extern int pcsp_sound_mixer; +#endif + int minor, major; + + /* if disabled in commandline */ + if (! pcsp_enabled) { + printk("PCSP-device disabled\n"); + return 0; + } + + /* first time pcsp is loaded */ + pcsp.first_boot = 1; + + if (! pcsp_register_driver()) + return 0; + + major = (PCSP_SOUND_VERSION >> 8); + minor = (PCSP_SOUND_VERSION & 0xFF); + if (! (minor & 0xF)) + minor >>= 4; + printk("PCSP %d.%x measurement:", major, minor); + pcsp_driver_init(); + if (pcsp_enabled) { + printk("PCSP %d.%x:", major, minor); + pcsp_device_init(); + printk(" DSP installed on device %d",pcsp_sound_dsp); +#if defined(PCSP_16BIT) || defined(PCSP_MIXER) + pcsp_mixer_init(); + printk(", Mixer installed on device %d", pcsp_sound_mixer); +#endif + printk("\n"); + } + return 0; +} + +#else + +/* + * this pcsp_init() function is called when the driver itself is + * compiled as a module once at kernel-startup + */ +int __init pcsp_init(void) +{ + int minor, major; + + /* first time pcsp is loaded */ + pcsp.first_boot = 1; + + major = (PCSP_SOUND_VERSION >> 8); + minor = (PCSP_SOUND_VERSION & 0xFF); + if (! (minor & 0xF)) + minor >>= 4; + printk("PCSP %d.%x measurement:", major, minor); + pcsp_driver_init(); + return 0; +} +#endif + +module_init(pcsp_init); diff -urN linux-2.4.19-clean/drivers/sound/pcsp_tables.h linux-2.4.19/drivers/sound/pcsp_tables.h --- linux-2.4.19-clean/drivers/sound/pcsp_tables.h Thu Jan 1 03:00:00 1970 +++ linux-2.4.19/drivers/sound/pcsp_tables.h Fri Aug 16 18:32:01 2002 @@ -0,0 +1,256 @@ +/* + * linux/drivers/sound/pcsp_tables.h + * + * /dev/pcsp implementation + * + * Copyright (C) 2002 Stas Sergeev + */ + +static unsigned char sp_tabs[][256] = { +#ifndef CONFIG_PCSP_HIGH_FRQ +{ /* linear */ +64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, +60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, +56, 56, 56, 55, 55, 55, 55, 54, 54, 54, 54, 53, 53, 53, 53, 52, +52, 52, 52, 51, 51, 51, 51, 50, 50, 50, 50, 49, 49, 49, 49, 48, +48, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 45, 44, +44, 44, 44, 43, 43, 43, 43, 43, 42, 42, 42, 42, 41, 41, 41, 41, +40, 40, 40, 40, 39, 39, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, +36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 34, 34, 33, 33, 33, 33, +32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, +28, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 26, 25, 25, 25, 25, +24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 22, 21, 21, 21, +21, 20, 20, 20, 20, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, +17, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13, +13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, + 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, + 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1 +}, +{ /* amp 1 */ +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, +63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, +62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 59, 59, 59, 59, +59, 58, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 55, 55, +55, 54, 54, 54, 54, 53, 53, 53, 52, 52, 52, 51, 51, 51, 51, 50, +50, 50, 49, 49, 49, 48, 48, 48, 47, 47, 47, 46, 46, 45, 45, 45, +44, 44, 44, 43, 43, 43, 42, 42, 41, 41, 41, 40, 40, 40, 39, 39, +38, 38, 38, 37, 37, 37, 36, 36, 35, 35, 35, 34, 34, 33, 33, 33, +32, 32, 32, 31, 31, 30, 30, 30, 29, 29, 28, 28, 28, 27, 27, 27, +26, 26, 25, 25, 25, 24, 24, 24, 23, 23, 22, 22, 22, 21, 21, 21, +20, 20, 20, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, +15, 14, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 11, 10, +10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, + 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 2 */ +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, +63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 62, 61, 61, 61, 61, +61, 60, 60, 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 57, 57, 57, +57, 56, 56, 56, 55, 55, 54, 54, 54, 53, 53, 52, 52, 52, 51, 51, +50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 43, 43, 42, +42, 41, 41, 40, 39, 39, 38, 38, 37, 36, 36, 35, 35, 34, 33, 33, +32, 32, 31, 30, 30, 29, 29, 28, 27, 27, 26, 26, 25, 24, 24, 23, +23, 22, 22, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, 15, 15, +14, 14, 13, 13, 13, 12, 12, 11, 11, 11, 10, 10, 9, 9, 9, 8, + 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, + 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 3 */ +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, +62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 58, 58, 58, 57, +57, 56, 56, 55, 55, 54, 54, 53, 52, 52, 51, 50, 50, 49, 48, 47, +47, 46, 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 35, 34, 33, +32, 31, 30, 29, 28, 27, 26, 25, 25, 24, 23, 22, 21, 20, 19, 18, +18, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 4 */ +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, +64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 62, 62, +62, 62, 61, 61, 61, 60, 60, 59, 59, 58, 58, 57, 56, 55, 55, 54, +53, 52, 51, 50, 48, 47, 46, 45, 43, 42, 41, 39, 38, 36, 35, 33, +32, 30, 29, 27, 26, 24, 23, 22, 20, 19, 18, 17, 15, 14, 13, 12, +11, 10, 10, 9, 8, 7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +} +#else +{ /* linear */ +32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, +30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, +28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, +26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, +24, 24, 24, 24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, +22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 20, +20, 20, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, +18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, +16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 15, 15, +14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, +13, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, +11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 +}, +{ /* amp 1 */ +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, +31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 29, +29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, +27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 25, 25, +25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 23, 23, 23, 23, 23, 23, +22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, +19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, +16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, +13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, +10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 2 */ +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, +31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, +30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 28, +28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 25, +25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21, +21, 21, 20, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, +16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 13, 13, 13, 13, 12, 12, +12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, + 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 3 */ +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, +31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, +28, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 25, 25, 25, 24, 24, +23, 23, 23, 22, 22, 21, 21, 20, 20, 20, 19, 19, 18, 18, 17, 17, +16, 16, 15, 15, 14, 14, 13, 13, 13, 12, 12, 11, 11, 10, 10, 10, + 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ /* amp 4 */ +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, +31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 27, 27, +26, 26, 25, 25, 24, 24, 23, 23, 22, 21, 20, 20, 19, 18, 18, 17, +16, 15, 15, 14, 13, 13, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, + 6, 6, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}, +{ +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 30, 30, +30, 29, 29, 28, 28, 27, 26, 25, 25, 24, 23, 22, 20, 19, 18, 17, +16, 15, 14, 13, 11, 10, 9, 8, 8, 7, 6, 5, 5, 4, 4, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +} +#endif +}; + +#define PCSP_MAX_GAIN (sizeof(sp_tabs)/sizeof(sp_tabs[0]) - 1) + +/* + * The tables were generated with this small program: + +#define _GNU_SOURCE + +#include +#include +#include + +#define MAX_VAL 64 +#define MIN_VAL 1 + +double sine(double i) { + return ((sin(((i * M_PI) / 255) - M_PI_2) + 1) / 2) * 255; +} + +int fit(double x) { + return lrint((x / 255) * (MAX_VAL - MIN_VAL) + MIN_VAL); +} + +int main(int argc, char *argv[]) { +int i, j, val, amp; +double tmp; + if (argc==2) + amp=atoi(argv[1]); + else + amp=0; + for(i=1;i<=256;i++) { + tmp = (double)(256-i); + for (j=0;j + +/* MUST BE defined from 0.5 */ +#define PCSP_SOUND_VERSION 0x104 /* read 1.04 */ + +/* card ID, real soundcards use 0 to ... */ + +#define SNDCARD_PCSP 128 +#define SNDCARD_STO1 129 +#define SNDCARD_DACM 130 +#define SNDCARD_DACS 131 +#define SNDCARD_STNC 132 + + +/* IOCTL for changing the play-device, real sample rate etc. */ + +#define PCSP_SET_DEV 0x00014350 +#define PCSP_GET_DEV 0x00024350 +#define PCSP_SET_PORTS 0x00034350 +#define PCSP_GET_PORTS 0x00044350 +#if 0 +#define PCSP_SET_VOL 0x00054350 +#define PCSP_GET_VOL 0x00064350 +#endif +#define PCSP_SET_SRATE 0x00074350 +#define PCSP_GET_SRATE 0x00084350 +#define PCSP_GET_MEASURE 0x00094350 +#define PCSP_SET_EMU_MODE 0x000A4350 +#define PCSP_GET_VERSION 0x000F4350 + +#define PCSP_EMULATION_OFF 0 +#define PCSP_EMULATION_ON 1 +#define PCSP_EMULATION_QUERY 2 + + + +#if defined(MODULE) || defined(__KERNEL__) + +/* + * If defined, pcsp will use an OSS fops rather than its native fops. + * It is more safe to use OSS fops, but using native fops will produce + * a better sound. + * WARNING: currently pcsp have some problems working with OSS fops. + * If you know how to fix it, please send me patches. + */ +#define USE_OSS_FOPS 0 +#define PCSP_16BIT 1 +#define PCSP_MIXER 1 +#define PCSP_LEFT_VOL 100 +#define PCSP_RIGHT_VOL 100 +#define PCSP_GAIN 100 + +/* the timer stuff */ +#define TIMER_IRQ 0 +#define SRATE (CLOCK_TICK_RATE / pcsp.timerCF) + +/* + * the default blocksize for playing thru + * PC-Speaker, STO1 and STNC use twice as much + */ + +#define ABLK_SIZE 16384 +#define PCSP_MAX_BUFFERS 512 +#define PCSP_TOTAL_SIZE 262144 +#define PCSP_INIT_BUFFERS (PCSP_TOTAL_SIZE / ABLK_SIZE) + +/* the default samplerate for /dev/audio */ +#ifndef PCSP_DEFAULT_RATE +#define PCSP_DEFAULT_RATE 8000 +#endif + +#ifndef PCSP_MIXER +#define PCSP_DEFAULT_LEFT 100 +#define PCSP_DEFAULT_RIGHT 100 +#endif + +/* + * the "critical" frequency: if the machine is too slow for this, PCSP + * is disabled + */ +#ifndef PCSP_CRITICAL_FREQ +#define PCSP_CRITICAL_FREQ 12500 +#endif + +struct pcsp_status { + volatile int last_clocks; + int allocated; /* memory allocated successfully */ + unsigned char *data; /* one large buffer */ + unsigned char *buf[PCSP_MAX_BUFFERS]; /* fragments */ + unsigned char *buffer; /* current fragment */ + volatile long long in[PCSP_MAX_BUFFERS]; /* fragments fill */ + volatile long long xfer; + volatile long long index; /* current playing position */ + unsigned enable_bits; /* for triggers */ + unsigned volume; /* volume for pc-speaker */ + unsigned left; /* left volume */ + unsigned right; /* right volume */ + unsigned gain; /* output gain */ + unsigned srate; /* sample rate */ + unsigned si; /* precalculated step const */ + unsigned timerC; /* hardware timer ticks for srate */ + unsigned timerCF; /* for fixed samplerate */ + unsigned act_dev; /* which device is playing */ + unsigned port; /* on which lp-port */ + unsigned portS; /* for Stereo */ + volatile unsigned cur_buf; /* fragment currently playing */ + volatile unsigned next_buf; /* next fragment to fill */ + unsigned maxrate; /* maximum real sample rate */ + unsigned audio_fmt; /* 16 or 8 bit */ + unsigned fmt_msk; /* supported data formats */ + unsigned ablk_size; /* length of one audio-buffer */ + unsigned frag_size; /* length of one audio-fragment */ + unsigned frag_cnt; /* number of fragments */ + unsigned char e; + volatile char timer_on; + char mode; /* Mono / Stereo */ + char stereo_emu; /* set if Stereo is emulated */ + char enable_emu; /* set if the emulation is enabled */ + char first_boot; /* first time loaded? */ +}; + +/* + * the globals + */ +extern char pcsp_speaker, pcsp_enabled; +extern volatile int pcsp_timer0_latch, pcsp_clockticks; +extern struct pcsp_status pcsp; + +/* in arch/i386/kernel/irq.c */ +extern int pcsp_set_irq(int (*func)(void)); +extern int pcsp_release_irq(void); + +/* in pcsndrv.c and pcsp_mixer.c */ +extern int pcsp_register_driver(void); +extern int pcsp_device_init(void); +extern int pcsp_mixer_init(void); + +#endif +#endif +#endif