diff -urN linux.orig/CREDITS linux/CREDITS --- linux.orig/CREDITS Sun Dec 30 11:48:01 2001 +++ linux/CREDITS Sun Dec 30 11:54:39 2001 @@ -363,6 +363,8 @@ N: Erik Inge Bolsr 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 diff -urN linux.orig/Documentation/Configure.help linux/Documentation/Configure.help --- linux.orig/Documentation/Configure.help Sun Dec 30 11:48:01 2001 +++ linux/Documentation/Configure.help Sun Dec 30 12:00:49 2001 @@ -18802,6 +18802,75 @@ Say Y or M if you have a Hammerfall, Hammerfall light or Hammerfall DSP card from RME. +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. + +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). + +PC speaker 16bit stereo emulation +CONFIG_PCSP_16BIT + The PCSP-driver can emulate 16bit stereo audio data on any output + device. It cannot play this data at the high resolution and converts + them to 8bit (and mono for mono devices), but allows to run + applications that want 16bit samples (DOOM of course, what else :-), + but you need at least a 486DX and a DX-2 if you have only the + PC-Speaker; it works great on my 486DX-2 with a Stereo-on-1, + really better than my SB 1.5 :-). + It's safe to say 'Y' if you have a Pentium or a faster machine. + +CONFIG_PCSP_MIXER + The PCSP-driver can simulate the /dev/mixer device. This is only + useful if you have Stereo-on-One or Stereo-DACs, because Mono-devices + will ignore the 2 volumes and use only the left volume (PC-Speaker + however use the mean value). + + The /dev/mixer simulation supports only the Master-Volume device. + If you don't include /dev/mixer support, you can use pcsel to + change only the PC-Speaker volume, DACs will play at 100 %. + The mixer emulation is enabled when you have choosen the 16bit + emulation. + + Disabling /dev/mixer support don't speed up anything, so it's save + to say 'Y'. + +PC speaker default left volume +CONFIG_PCSP_LEFT + The startup volume for the left channel in %. + +PC speaker default right volume +CONFIG_PCSP_RIGHT + The startup volume for the right channel in %. + Are you using a crosscompiler CONFIG_CROSSCOMPILE Say Y here if you are compiling the kernel on a different diff -urN linux.orig/Documentation/sound/pcspeaker linux/Documentation/sound/pcspeaker --- linux.orig/Documentation/sound/pcspeaker Thu Jan 1 01:00:00 1970 +++ linux/Documentation/sound/pcspeaker Sun Dec 30 12:37:53 2001 @@ -0,0 +1,61 @@ +Tools for configuration of the PC speaker devices, along with notes and circuit +diagrams for those wishing to build their own DAC device to attach to the +parallel port, are available at + http://www.imladris.demon.co.uk/pcsp/pcsnd-kit.tar.gz + +All the hard work on this driver was performed by Michael Beck, and it was +ported to work with Alan Cox's soundcore subsystem in 2.1 kernels by +David Woodhouse . + +All that remains relevant of Michael's original README file is the following: + +Feel free to contact me (flames about my english and the useless of this +driver will be redirected to /dev/null, oh no, it's full...). + +Michael Beck beck@dresearch.de + +--- +2001-12-30 David Jez +NOTE: I ported this patch for 2.4.17 kernel. + please send me any bugreports. + +enjoy it! +Dave + +PS: Patch for old pcsnd-kit: +diff -urN pcsnd-kit.orig/Makefile pcsnd-kit/Makefile +--- pcsnd-kit.orig/Makefile Mon Feb 12 23:41:15 1996 ++++ pcsnd-kit/Makefile Fri Dec 28 21:14:23 2001 +@@ -4,7 +4,7 @@ + # + + CC=gcc +-CFLAGS=-O2 -Wall -s -N ++CFLAGS=-O2 -Wall -D CONFIG_PCSP + BINDIR=/usr/local/bin + MANDIR=/usr/man/man1 + DFLDEV=/dev/dsp +@@ -15,7 +15,7 @@ + $(CC) $(CFLAGS) -DAUDIO=\"${DFLDEV}\" vplay.c -o vplay + + pcsel: pcsel.c +- $(CC) $(CFLAGS) pcsel.c -o pcsel ++ $(CC) $(CFLAGS) -DAUDIO=\"${DFLDEV}\" pcsel.c -o pcsel + + randomize: random.c + $(CC) $(CFLAGS) random.c -o randomize +diff -urN pcsnd-kit.orig/pcsel.c pcsnd-kit/pcsel.c +--- pcsnd-kit.orig/pcsel.c Wed Apr 29 01:33:45 1998 ++++ pcsnd-kit/pcsel.c Fri Dec 28 21:14:07 2001 +@@ -13,8 +13,11 @@ + #include + #include + #include ++#include + ++#ifndef AUDIO + #define AUDIO "/dev/pcsp" ++#endif + + char *version = "pcsel 0.9c (12 Oct 95)\n"; + int audio, omode = O_WRONLY; diff -urN linux.orig/Makefile linux/Makefile --- linux.orig/Makefile Sun Dec 30 11:48:01 2001 +++ linux/Makefile Sun Dec 30 12:03:53 2001 @@ -157,6 +157,11 @@ endif DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o +ifneq ($(CONFIG_SOUND),y) +ifdef CONFIG_PCSP +DRIVERS-y += drivers/sound/sounddrivers.o +endif +endif DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o diff -urN linux.orig/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- linux.orig/arch/i386/kernel/irq.c Sun Dec 30 11:44:37 2001 +++ linux/arch/i386/kernel/irq.c Sun Dec 30 12:20:35 2001 @@ -1198,3 +1198,82 @@ register_irq_proc(i); } +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) +/* + * /dev/pcsp implementation, part of linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1993-1997 Michael Beck + */ + +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; +} +#endif diff -urN linux.orig/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c --- linux.orig/arch/i386/kernel/time.c Sun Dec 30 11:44:53 2001 +++ linux/arch/i386/kernel/time.c Sun Dec 30 12:22:30 2001 @@ -152,6 +152,10 @@ * comp.protocols.time.ntp! */ +#if defined(CONFIG_PCSP) || defined(CONFIG_PCSP_MODULE) +extern int pcsp_clockticks, pcsp_timer0_latch; +#endif + static unsigned long do_slow_gettimeoffset(void) { int count; @@ -242,6 +246,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; diff -urN linux.orig/drivers/char/joystick/analog.c linux/drivers/char/joystick/analog.c --- linux.orig/drivers/char/joystick/analog.c Sun Dec 30 11:44:39 2001 +++ linux/drivers/char/joystick/analog.c Sun Dec 30 12:18:01 2001 @@ -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.orig/drivers/char/joystick/gameport.c linux/drivers/char/joystick/gameport.c --- linux.orig/drivers/char/joystick/gameport.c Sun Dec 30 11:42:57 2001 +++ linux/drivers/char/joystick/gameport.c Sun Dec 30 12:18:31 2001 @@ -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.orig/drivers/char/mem.c linux/drivers/char/mem.c --- linux.orig/drivers/char/mem.c Sun Dec 30 11:48:03 2001 +++ linux/drivers/char/mem.c Sun Dec 30 12:37:44 2001 @@ -41,6 +41,9 @@ #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR) extern void tapechar_init(void); #endif +#ifdef CONFIG_PCSP +extern void pcsp_init(void); +#endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) @@ -671,6 +674,9 @@ #endif #if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR) tapechar_init(); +#endif +#if defined (CONFIG_PCSP) + pcsp_init(); #endif return 0; } diff -urN linux.orig/drivers/char/vt.c linux/drivers/char/vt.c --- linux.orig/drivers/char/vt.c Sun Dec 30 11:44:55 2001 +++ linux/drivers/char/vt.c Sun Dec 30 12:23:04 2001 @@ -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.orig/drivers/sound/Config.in linux/drivers/sound/Config.in --- linux.orig/drivers/sound/Config.in Sun Dec 30 11:48:08 2001 +++ linux/drivers/sound/Config.in Sun Dec 30 11:57:57 2001 @@ -6,6 +6,22 @@ # Prompt user for primary drivers. +if [ "$ARCH" = "i386" ]; then + dep_tristate 'Internal PC speaker support' CONFIG_PCSP $CONFIG_SOUND +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 + bool ' Enable 16bit stereo emulation support' CONFIG_PCSP_16BIT + if [ "$CONFIG_PCSP_16BIT" = "n" ] ; then + bool ' Enable /dev/mixer simulation' CONFIG_PCSP_MIXER + fi + int ' Left volume at startup [0-100]' CONFIG_PCSP_LEFT 100 + int ' Right volume at startup [0-100]' CONFIG_PCSP_RIGHT 100 +fi + dep_tristate ' BT878 audio dma' CONFIG_SOUND_BT878 $CONFIG_SOUND dep_tristate ' C-Media PCI (CMI8338/8738)' CONFIG_SOUND_CMPCI $CONFIG_SOUND $CONFIG_PCI if [ "$CONFIG_SOUND_CMPCI" = "y" -o "$CONFIG_SOUND_CMPCI" = "m" ]; then diff -urN linux.orig/drivers/sound/Makefile linux/drivers/sound/Makefile --- linux.orig/drivers/sound/Makefile Sun Dec 30 11:48:08 2001 +++ linux/drivers/sound/Makefile Sun Dec 30 12:57:51 2001 @@ -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. @@ -74,6 +74,7 @@ obj-$(CONFIG_SOUND_BT878) += btaudio.o obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o +obj-$(CONFIG_PCSP) += pcsnd.o ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o @@ -100,7 +101,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 \ @@ -109,6 +110,9 @@ sequencer.o sequencer_syms.o sound_timer.o sys_timer.o soundcore-objs := sound_core.o sound_firmware.o +ifdef CONFIG_PCSP + obj-y += pcsp_stub.o +endif gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o @@ -116,6 +120,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 @@ -149,6 +154,9 @@ wavefront.o: $(wavefront-objs) $(LD) -r -o $@ $(wavefront-objs) + +pcsnd.o: $(pcsnd-objs) + $(LD) -r -o $@ $(pcsnd-objs) # Firmware files that need translation # diff -urN linux.orig/drivers/sound/pcsndriv.c linux/drivers/sound/pcsndriv.c --- linux.orig/drivers/sound/pcsndriv.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/sound/pcsndriv.c Sun Dec 30 12:47:58 2001 @@ -0,0 +1,1021 @@ +/* + * 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 "pcsp_tables.h" + +/* + * 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_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 CONFIG_PCSP_16BIT +#define CONFIG_PCSP_MIXER +static unsigned char left_volS[256], right_volS[256]; +#endif + +#ifdef CONFIG_PCSP_MIXER +extern int pcsp_mixer_set(int whichDev, unsigned int level); +extern int pcsp_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); +#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); + +/* 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); +} + +/* the timer-int for playing thru PC-Speaker */ +static int pcsp_do_timer(void) +{ + if (pcsp.index < pcsp.in[pcsp.actual]) { + outb(pcsp.e, 0x61); + outb(pcsp.e ^ 1, 0x61); + outb(vl_tab[pcsp.buffer[pcsp.index]], 0x42); + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.actual]) { + pcsp.xfer = pcsp.index = 0; + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + if (waitqueue_active(&pcsp_sleep)) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + + if ( (pcsp_clockticks -= pcsp.timerCF) < 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.buffer < pcsp.end) { + if (pcsp.mode) { + outb(right_vol[*pcsp.buffer++], 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 :-( + * + */ + + if ( (pcsp_clockticks -= pcsp.timerC) < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + + PCSP_CPU_DELAY; + + outb(left_vol[*pcsp.buffer++], pcsp.port); + outb(2, pcsp.port + 2); + outb(0, pcsp.port + 2); + } + else { /* Mono */ + outb(left_vol[*pcsp.buffer++], pcsp.port); + if ( (pcsp_clockticks -= pcsp.timerC) < 0) { + pcsp_clockticks += LATCH; + ret = 0; + } + else + ret = 1; + } + } + if (pcsp.buffer >= pcsp.end) { + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + if (waitqueue_active(&pcsp_sleep)) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + return (ret); +} + +/* timer-int for playing thru DACs */ +static int pcsp_do_dac_timer(void) +{ + if (pcsp.buffer < pcsp.end) { + if (pcsp.act_dev == SNDCARD_DACS) { + if (pcsp.mode) { + outb(left_vol[*pcsp.buffer++], pcsp.port); + outb(right_vol[*pcsp.buffer++], pcsp.portS); + } + else { + outb(left_vol[*pcsp.buffer], pcsp.port); + outb(left_vol[*pcsp.buffer++], pcsp.portS); + } + } + else /* Simple DAC */ + outb(left_vol[*pcsp.buffer++], pcsp.port); + } + if (pcsp.buffer >= pcsp.end) { + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + if (waitqueue_active(&pcsp_sleep)) + wake_up_interruptible(&pcsp_sleep); + if (pcsp.in[pcsp.actual] == 0) + pcsp_stop_timer(); + } + + if ( (pcsp_clockticks -= pcsp.timerC) < 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.timerCF = pcsp.realrate; + pcsp.si = (pcsp.srate << 16) / SRATE; + return pcsp.srate; +} + +static void pcsp_start_timer(void) +{ + int result; + + /* use the first buffer */ + pcsp.actual = 0; + pcsp.xfer = pcsp.index = 0; + pcsp.buffer = pcsp.buf[pcsp.actual]; + pcsp.end = pcsp.buffer + pcsp.in[pcsp.actual]; + + 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; +} + +/* reset the timer to 100 Hz and reset old timer-int */ +static void pcsp_stop_timer(void) +{ + 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; + } + + /* reset the buffer */ + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.xfer = pcsp.index = 0; + pcsp.actual = 0; + pcsp.buffer = pcsp.end = pcsp.buf[pcsp.actual]; + + pcsp_speaker = 0; +} + +/* + calculate a translation-table for PC-Speaker +*/ +static void pcsp_calc_vol(int volume) +{ + int i, j; + + if (pcsp.is_ulaw) + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + if (j < -128) + j = -128; + if (j > 127) + j = 127; + vl_tab[i] = sp_tab[ulaw[j + 128]]; + } + else + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + if (j < -128) + j = -128; + if (j > 127) + j = 127; + vl_tab[i] = sp_tab[j + 128]; + } +} + +/* calculate linear translation table for DACs */ +static void pcsp_calc_voltab(int volume, unsigned char *tab) +{ + int i, j; + + if (pcsp.is_ulaw) + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + *tab++ = ulaw[j + 128]; + } + else + for (i = 0; i < 256; ++i) { + j = ((i - 128) * volume) >> 8; + *tab++ = j + 128; + } +} + +static inline void pcsp_set_voltables(void) +{ +#ifdef CONFIG_PCSP_16BIT + if (pcsp.stereo_emu) { + pcsp_calc_voltab(pcsp.left, left_volS); + pcsp_calc_voltab(pcsp.right, right_volS); + pcsp_calc_vol(PCSP_MAX_VOLUME); + pcsp_calc_voltab(PCSP_MAX_VOLUME, left_vol); + pcsp_calc_voltab(PCSP_MAX_VOLUME, right_vol); + } + else +#endif + { + pcsp_calc_voltab(pcsp.left, left_vol); + pcsp_calc_voltab(pcsp.right, right_vol); + pcsp_calc_vol(pcsp.volume); + } +} + +#ifdef CONFIG_PCSP_MIXER + +/* this is called if /dev/pcmixer change Mastervolume */ +inline void pcsp_set_volume(unsigned short v) +{ + pcsp.left = (((unsigned)(v & 0x7F) << 8) + 50) / 100; + pcsp.right = (((unsigned)(v & 0x7F00)) + 50) / 100; + pcsp.volume = (pcsp.right + pcsp.left) >> 1; + pcsp_set_voltables(); +} + +inline unsigned pcsp_get_mode(void) +{ + return pcsp.mode; +} +#endif + +/* + * set the speed for /dev/pcsp, it's now from 4000 - 44100 Hz, + * but DAC's are bounded by the maximal samplerate + */ +inline unsigned long pcsp_set_speed(unsigned long speed) +{ + if (speed < 4000) + speed = 4000; + if (speed > 44100) + speed = 44100; + if (pcsp.act_dev != SNDCARD_PCSP) + if (speed > pcsp.maxrate) + speed = pcsp.maxrate; + return speed; +} + +/* + * set the audio type + */ +int pcsp_set_format(int fmt) +{ + static char ulaw; + + if (fmt != AFMT_QUERY) { + ulaw = 0; + if (! (pcsp.fmt_msk & fmt)) { /* Not supported */ + if (fmt == AFMT_MU_LAW) + ulaw = 1; + else + fmt = AFMT_U8; /* this is supported */ + } + pcsp.audio_fmt = fmt; + /* + we must recalculate the volume-tables is we change + ulaw-state + */ + if (pcsp.is_ulaw != ulaw) { + pcsp.is_ulaw = ulaw; + 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 CONFIG_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 + return 0; +} + +/* + * wait until the complete buffers are played or a signal has arrised + */ +static void pcsp_sync(void) +{ + while (! signal_pending(current) && + (pcsp.in[0] || pcsp.in[1]) ) { + /* Wait until a complete block are ready */ + interruptible_sleep_on(&pcsp_sleep); + } +} + +/* + * the driver functions + */ +static int pcsp_release(struct inode * inode, struct file * file) +{ + pcsp_sync(); + pcsp_stop_timer(); + outb_p(0xb6,0x43); /* binary, mode 2, LSB/MSB, ch 2 */ + + vfree(pcsp.buf[0]); + vfree(pcsp.buf[1]); + + pcsp_active = 0; + MOD_DEC_USE_COUNT; + return 0; +} + +static int pcsp_open(struct inode * inode, struct file * file) +{ + int minor = MINOR(inode->i_rdev); + + if (pcsp_active) + return -EBUSY; + + switch (minor & 0xf) { + case 3: /* DSP device /dev/dsp* */ +#ifdef CONFIG_PCSP_16BIT + if (pcsp_set_format(AFMT_S16_LE) != AFMT_S16_LE) +#endif + pcsp_set_format(AFMT_U8); + break; + case 4: /* Sun Audio device /dev/audio* */ + pcsp_set_format(AFMT_MU_LAW); /* input is ULAW */ + break; + + default: + printk(KERN_WARNING "PCSP: minor %d (%d) is for unknown device\n",minor,minor& 0xf); + return -ENODEV; + } + + if (! (pcsp.buf[0] = vmalloc(pcsp.ablk_size))) + return -ENOMEM; + if (! (pcsp.buf[1] = vmalloc(pcsp.ablk_size))) { + vfree(pcsp.buf[0]); + return -ENOMEM; + } + + pcsp.buffer = pcsp.end = pcsp.buf[0]; + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.timer_on = + pcsp.frag_size = + pcsp.frag_cnt = 0; + + /* we set 8000 Hz for /dev/audio (ulaw flag set before open) */ + if (pcsp.is_ulaw) + pcsp_calc_srate(PCSP_DEFAULT_RATE); + + pcsp_active = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int pcsp_ioctl_out(unsigned long *ptr, long value) +{ + static int error; + + if (value < 0) + return value; + error = verify_area(VERIFY_WRITE, ptr, sizeof(long)); + if (error) + return error; + put_user(value, (long *)ptr); + return 0; +} + + +/* + * the new version 2 IOCTL's + */ +static int pcsp_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + unsigned long ret; + unsigned long *ptr = (unsigned long *)arg; + int i; + +#ifdef CONFIG_PCSP_MIXER + if (((cmd >> 8) & 0xff) == 'M') /* it's a Mixer IOCTL */ + return pcsp_mixer_ioctl(inode, file, cmd, arg); +#endif + switch (cmd) { + case SNDCTL_DSP_SPEED: + if (get_user(arg, ptr)) + return -EFAULT; + arg = pcsp_set_speed(arg); + arg = pcsp_calc_srate(arg); + return pcsp_ioctl_out(ptr, arg); + + case SOUND_PCM_READ_RATE: + return pcsp_ioctl_out(ptr, pcsp.srate); + + case SNDCTL_DSP_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 pcsp_ioctl_out(ptr, pcsp.mode + 1); + + case SNDCTL_DSP_GETBLKSIZE: + return pcsp_ioctl_out(ptr, pcsp.frag_size ? + pcsp.frag_size : pcsp.ablk_size); + + case SNDCTL_DSP_SYNC: /* syncing, so speed changes work correct */ + pcsp_sync(); + pcsp_stop_timer(); + return (0); + + case SNDCTL_DSP_RESET: /* stops output immediately */ + pcsp_stop_timer(); + pcsp_calc_srate(PCSP_DEFAULT_RATE); + return (0); + + case SNDCTL_DSP_GETFMTS: + return pcsp_ioctl_out(ptr, pcsp.fmt_msk); + + case SNDCTL_DSP_SETFMT: + if (get_user(arg, ptr)) + return -EFAULT; + return pcsp_ioctl_out(ptr, pcsp_set_format(arg)); + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + case SNDCTL_DSP_NONBLOCK: + /* currently unsupported */ + return (-EINVAL); + + case SNDCTL_DSP_GETCAPS: + { + int info = 1; /* Revision level of this ioctl() */ + /* no further features :-) */ + return pcsp_ioctl_out(ptr, info); + } + + case SOUND_PCM_READ_BITS: + if (pcsp.audio_fmt == AFMT_MU_LAW) + return pcsp_ioctl_out(ptr, AFMT_U8); + return pcsp_ioctl_out(ptr, pcsp.audio_fmt); + + /* + the following ioctls currently do nothing, but + exist for compatibility; however because pcsp's + implementation is somewhat strange they are not needed, + because pcsp start output after any count of data + written to one buffer + this may change in the future + */ + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_POST: + return (0); + + case SNDCTL_DSP_SETFRAGMENT: + { + int bytes, count; + int fact; + + if (get_user(fact, (int *)arg)) + 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) + return -EINVAL; + + pcsp.frag_size = (1 << bytes); + pcsp.frag_cnt = /*count*/ 2; + + if (pcsp.frag_size > pcsp.ablk_size) + pcsp.frag_size = pcsp.ablk_size; + + return pcsp_ioctl_out(ptr, bytes | (count << 16)); + } + + 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); + pcsp_calc_srate(pcsp.srate); + return (0); + + case PCSP_GET_DEV: + return pcsp_ioctl_out(ptr, pcsp.act_dev); + + 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 pcsp_ioctl_out(ptr, ret); + + case PCSP_GET_VOL: + return pcsp_ioctl_out(ptr, pcsp.volume); + + case PCSP_SET_VOL: + if (get_user(pcsp.volume, ptr)) + return -EFAULT; +#ifdef CONFIG_PCSP_MIXER + arg = MIN(256, pcsp.volume); + arg = (arg * 100) / 256; + arg = (arg << 8 ) | arg; + pcsp_mixer_set(SOUND_MIXER_VOLUME, arg); +#else + pcsp_calc_vol(pcsp.volume); +#endif + return (0); + + case PCSP_GET_SRATE: + return pcsp_ioctl_out(ptr, SRATE); + + case PCSP_SET_SRATE: + if (get_user(arg, ptr)) + return -EFAULT; + if (arg < 10000 || arg > MAX_SRATE || arg > pcsp.maxrate) + return (-EINVAL); + pcsp.realrate = (CLOCK_TICK_RATE + arg / 2) / arg; + return (0); + + case PCSP_GET_MEASURE: + return pcsp_ioctl_out(ptr, pcsp.maxrate); + + case PCSP_SET_EMU_MODE: +#ifdef CONFIG_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 pcsp_ioctl_out(ptr, pcsp.enable_emu); +#endif + case PCSP_GET_VERSION: + return pcsp_ioctl_out(ptr, PCSP_SOUND_VERSION); + + 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) +{ + size_t copy_size; + unsigned long max_copy_size; + unsigned long total_bytes_written = 0; + unsigned bytes_written; + unsigned l, r; + unsigned char *p; + int i, j; +#ifdef CONFIG_PCSP_16BIT + int factor = 1; + +#define CNT(x) ((x) * factor) +#else +#define CNT(x) (x) +#endif + +#ifdef CONFIG_PCSP_16BIT + /* the audio format shouldn't be changed during output */ + if (pcsp.audio_fmt == AFMT_S16_LE) { + count >>= 1; + factor <<= 1; + } + if (pcsp.stereo_emu) { + count >>= 1; + factor <<= 1; + } +#endif + + max_copy_size = pcsp.frag_size ? pcsp.frag_size : pcsp.ablk_size; + do { + bytes_written = 0; + copy_size = (count <= max_copy_size) ? count : max_copy_size; + i = pcsp.in[0] ? 1 : 0; + if (copy_size && !pcsp.in[i]) { + +#ifdef CONFIG_PCSP_16BIT + if (pcsp.audio_fmt == AFMT_S16_LE) + if (pcsp.stereo_emu) { + p = pcsp.buf[i]; + for (j = 0; j < copy_size; ++j) { + get_user(l, (unsigned char *)&buffer[4*j + 1]); + get_user(r, (unsigned char *)&buffer[4*j + 3]); + *p++ = (left_volS[l^0x80] + right_volS[r^0x80]) >> 1; + } + } + else { + p = pcsp.buf[i]; + for (j = 0; j < copy_size; ++j) { + get_user(*p, &buffer[2*j + 1]); + *p++ ^= 0x80; + } + } + else if (pcsp.stereo_emu) { + p = pcsp.buf[i]; + for (j = 0; j < copy_size; ++j) { + get_user(l, (unsigned char *)&buffer[2*j + 0]); + get_user(r, (unsigned char *)&buffer[2*j + 1]); + *p++ = (left_volS[l] + right_volS[r]) >> 1; + } + } + else +#endif + copy_from_user(pcsp.buf[i], buffer, copy_size); + pcsp.in[i] = copy_size; + if (! pcsp.timer_on) + pcsp_start_timer(); + bytes_written += copy_size; + buffer += copy_size; + } + + if (pcsp.in[0] && pcsp.in[1]) { + interruptible_sleep_on(&pcsp_sleep); + if (signal_pending(current)) { + if (total_bytes_written + bytes_written) + return CNT(total_bytes_written + bytes_written); + else + return -EINTR; + } + } + total_bytes_written += bytes_written; + count -= bytes_written; + + } while (count > 0); + return CNT(total_bytes_written); +#undef CNT +} + +struct file_operations pcsp_dsp_fops = { + read: pcsp_read, + write: pcsp_write, + ioctl: pcsp_ioctl, + open: pcsp_open, + release: pcsp_release +}; + +int __init pcsp_device_init(void) +{ + int i; + + if (! pcsp_enabled) + return 0; + + /* do we need a first-time initialisation? */ + if (! pcsp.first_boot) + return 0; + + init_waitqueue_head(&pcsp_sleep); + + pcsp.first_boot = 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.is_ulaw = 0; + pcsp.audio_fmt = AFMT_U8; +#ifdef CONFIG_PCSP_16BIT + pcsp.fmt_msk = AFMT_U8 | AFMT_S16_LE; +#else + pcsp.fmt_msk = AFMT_U8; +#endif + pcsp.buffer = pcsp.buf[0]; + pcsp.in[0] = pcsp.in[1] = 0; + pcsp.actual = 0; + pcsp.act_dev = SNDCARD_PCSP; + pcsp.port = pcsp.portS = 0; + pcsp.left = (CONFIG_PCSP_LEFT * 256 + 50) / 100; + pcsp.right = (CONFIG_PCSP_RIGHT * 256 + 50) / 100; + pcsp.volume = (pcsp.left + pcsp.right) >> 1; + pcsp.ablk_size = ABLK_SIZE; + pcsp.frag_size = 0; + pcsp.frag_cnt = 0; + pcsp_calc_srate(PCSP_DEFAULT_RATE); + pcsp_calc_vol(pcsp.volume); + pcsp_calc_voltab(CONFIG_PCSP_LEFT, left_vol); + pcsp_calc_voltab(CONFIG_PCSP_RIGHT, right_vol); +#ifdef CONFIG_PCSP_16BIT + memcpy(left_volS, left_vol, sizeof(left_volS)); + memcpy(right_volS, right_vol, sizeof(right_volS)); + 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.orig/drivers/sound/pcsp.c linux/drivers/sound/pcsp.c --- linux.orig/drivers/sound/pcsp.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/sound/pcsp.c Sun Dec 30 13:52:13 2001 @@ -0,0 +1,94 @@ +/* + * linux/drivers/sound/pcsp.c + * + * /dev/pcsp implementation + * + * Copyright (C) 1993-1997 Michael Beck + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef MODULE +#include +#include +#include + +MODULE_AUTHOR("Michael Beck "); +MODULE_DESCRIPTION("Alternate Sound Driver for Linux 2.4 Version 1.3"); +MODULE_SUPPORTED_DEVICE("pcsp"); +EXPORT_NO_SYMBOLS; +MODULE_LICENSE("GPL"); +#endif + +/* 16bit emulation needs the mixer */ +#ifdef CONFIG_PCSP_16BIT +#define CONFIG_PCSP_MIXER +#endif + +extern struct file_operations pcsp_dsp_fops, pcsp_mixer_fops; +extern long pcsp_driver_init(long); +extern int pcsp_set_format(int); +extern void pcsp_free_ports(void); + + +#ifdef MODULE + +static int pcsp_sound_dsp, pcsp_sound_mixer; + +int __init init_module(void) +{ + int minor, major; + + if (! pcsp_enabled) { + printk("pcsp disabled\n"); + return -ENODEV; + } + + pcsp.first_boot = 1; + if ((pcsp_sound_dsp = register_sound_dsp(&pcsp_dsp_fops, -1))< 0) { + printk(KERN_WARNING "Unable to register PC speaker DSP device\n"); + return pcsp_sound_dsp; + } + printk(KERN_DEBUG "PCSP on device %d\n",pcsp_sound_dsp); + +#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER) + if ((pcsp_sound_mixer = register_sound_mixer (&pcsp_mixer_fops, -1))<0) { + printk(KERN_WARNING "Unable to register PC speaker mixer device\n"); + unregister_sound_dsp(pcsp_sound_dsp); + return pcsp_sound_mixer; + } + printk(KERN_DEBUG "PCSP mixer on device %d\n", pcsp_sound_mixer); +#endif + major = (PCSP_SOUND_VERSION >> 8); + minor = (PCSP_SOUND_VERSION & 0xFF); + if (! (minor & 0xF)) + minor >>= 4; + + pcsp_driver_init(0); + + pcsp_device_init(); +#ifdef CONFIG_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(); + unregister_sound_dsp(pcsp_sound_dsp); + unregister_sound_mixer (pcsp_sound_mixer); + } +} +#endif diff -urN linux.orig/drivers/sound/pcsp_mixer.c linux/drivers/sound/pcsp_mixer.c --- linux.orig/drivers/sound/pcsp_mixer.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/sound/pcsp_mixer.c Sun Dec 30 12:22:09 2001 @@ -0,0 +1,157 @@ +/* + * linux/drivers/sound/pcsp_mixer.c + * + * /dev/pcsp implementation - simple Mixer routines + * + * (C) 1993-1997 Michael Beck + * Craig Metz (cmetz@thor.tjhsst.edu) + */ + +#include + +#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER) + +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#define __NO_VERSION__ +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define IOCTL_OUT(arg, ret) (put_user(ret, (long *)arg)) +#define OFF 0 + +static int rec_devices = 0; /* No recording source */ + +static struct { + char active; +} pcmixer; + +#define POSSIBLE_RECORDING_DEVICES (0) +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_VOLUME) + +static unsigned short levels[SOUND_MIXER_NRDEVICES] = +{ + 0, /* 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 */ +}; + +extern void pcsp_set_volume(unsigned short); +extern unsigned pcsp_get_mode(void); + +int pcsp_mixer_set(int whichDev, unsigned int level) +{ + int left, right; + + left = level & 0x7f; + right = (level & 0x7f00) >> 8; + + switch (whichDev) { + case SOUND_MIXER_VOLUME: /* Master volume (0-127) */ + levels[whichDev] = left | (right << 8); + pcsp_set_volume(levels[whichDev]); + break; + + default: + return (-EINVAL); + } + return (levels[whichDev]); +} + +int pcsp_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int val; + long *ptr = (long *)arg; + + if (((cmd >> 8) & 0xff) == 'M') { + if (cmd & IOC_IN) { + if (get_user(val, ptr)) + return -EFAULT; + if ((cmd & 0xff)< SOUND_MIXER_NRDEVICES) + return IOCTL_OUT(arg, pcsp_mixer_set(cmd & 0xff, val)); + else { + return (-EINVAL); + } + } + + switch (cmd & 0xff) { + + case SOUND_MIXER_RECSRC: + return IOCTL_OUT(arg, rec_devices); + + case SOUND_MIXER_STEREODEVS: + return IOCTL_OUT(arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE)); + + case SOUND_MIXER_DEVMASK: + return IOCTL_OUT(arg, SUPPORTED_MIXER_DEVICES); + + case SOUND_MIXER_RECMASK: + return IOCTL_OUT(arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES); + + case SOUND_MIXER_CAPS: + return IOCTL_OUT(arg, 0); + + default: + if ((cmd & 0xff) < SOUND_MIXER_NRDEVICES) + return IOCTL_OUT(arg, levels[cmd & 0xff]); + else { + return (-EINVAL); + } + } + } + + return (-EINVAL); +} + +static int pcsp_mixer_release(struct inode * inode, struct file * file) +{ + --pcmixer.active; + MOD_DEC_USE_COUNT; + return 0; +} + +static int pcsp_mixer_open(struct inode * inode, struct file * file) +{ + if (pcmixer.active) + return (-EBUSY); + ++pcmixer.active; + MOD_INC_USE_COUNT; + return 0; +} + +struct file_operations pcsp_mixer_fops = { + ioctl: pcsp_mixer_ioctl, + open: pcsp_mixer_open, + release: pcsp_mixer_release +}; + +int pcsp_mixer_init(void) +{ + levels[0] = ((pcsp.right*100 + 128) & ~0xFF) | ((pcsp.left*100 + 128) >> 8); + pcsp_mixer_set(SOUND_MIXER_VOLUME, levels[SOUND_MIXER_VOLUME]); + pcmixer.active = 0; + return 0; +} + +#endif diff -urN linux.orig/drivers/sound/pcsp_stub.c linux/drivers/sound/pcsp_stub.c --- linux.orig/drivers/sound/pcsp_stub.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/sound/pcsp_stub.c Sun Dec 30 12:40:17 2001 @@ -0,0 +1,323 @@ +/* + * 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 +#ifndef CONFIG_PCSP_MODULE +#include +#endif + +#include +#include + +/* + * need this macros + */ +#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) ) +#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) ) + +/* + * need this to be global + */ +char pcsp_active = 0; +char pcsp_speaker = 0; +char pcsp_enabled = -1; +volatile int pcsp_timer0_latch, pcsp_clockticks; +volatile int pcsp_test_running; + +struct pcsp_status pcsp; + +extern struct file_operations pcsp_dsp_fops, pcsp_mixer_fops; + + +#ifndef CONFIG_PCSP_NO_TEST_SPEED + +static void *sleep = NULL; + +/* + 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.actual]) { + outb(pcsp.e, 0x61); + outb(pcsp.e ^ 1, 0x61); + outb(pcsp.buffer[pcsp.buffer[pcsp.index]], 0x42); + + pcsp.xfer += pcsp.si; + pcsp.index = pcsp.xfer >> 16; + } + if (pcsp.index >= pcsp.in[pcsp.actual]) { + pcsp.xfer = pcsp.index = 0; + pcsp.in[pcsp.actual] = 0; + pcsp.actual ^= 1; + pcsp.buffer = pcsp.buf[pcsp.actual]; + if (sleep) /* NEVER */ + nop(); + if (pcsp.in[pcsp.actual] == 0xFFFF) + pcsp.actual ^= 1; + } + + if ( (pcsp_clockticks -= pcsp.timerCF) < 0) + pcsp_clockticks += LATCH; + ++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_clockticks = 0; + 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.actual = 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.realrate = best * 10 / 9; + pcsp.maxrate = CLOCK_TICK_RATE / pcsp.realrate; + printk(" maximal samplerate %d Hz", pcsp.maxrate); + + if (pcsp.maxrate > PCSP_CRITICAL_FREQ) { + if (MIN_CONST > pcsp.realrate) { + pcsp.realrate = 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) +{ + if (pcsp_enabled < 0) { +#ifndef CONFIG_PCSP_NO_TEST_SPEED + pcsp_enabled = pcsp_test_speed(); +#else + pcsp.maxrate = 44100; /* only a BIG freq */ + pcsp.realrate = (CLOCK_TICK_RATE + CONFIG_PCSP_SRATE / 2) / + CONFIG_PCSP_SRATE; + pcsp_enabled = 1; +#endif + } + else { + pcsp.realrate = MIN(pcsp.maxrate, MAX_SRATE); + pcsp.realrate = (CLOCK_TICK_RATE + pcsp.realrate / 2) / + pcsp.realrate; + } +} + +extern int pcsp_set_irq(int (*func)(void)); +extern int pcsp_release_irq(void); + +/* + * 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_active); +EXPORT_SYMBOL(pcsp_speaker); +EXPORT_SYMBOL(pcsp_driver_init); +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) +{ + int minor, major, pcsp_sound_dsp; +#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER) + int pcsp_sound_mixer; +#endif + /* 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_sound_dsp = register_sound_dsp(&pcsp_dsp_fops, -1))< 0) { + printk(KERN_WARNING "Unable to register PC speaker DSP device\n"); + return pcsp_sound_dsp; + } +#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER) + if ((pcsp_sound_mixer = register_sound_mixer (&pcsp_mixer_fops, -1))<0) { + printk(KERN_WARNING "Unable to register PC speaker mixer device\n"); + unregister_sound_dsp(pcsp_sound_dsp); + return pcsp_sound_mixer; + } +#endif + + 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(" installed at char-major-14-%d",pcsp_sound_dsp); +#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER) + pcsp_mixer_init(); + printk(", PCSP-mixer at char-major-14-%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.orig/drivers/sound/pcsp_tables.h linux/drivers/sound/pcsp_tables.h --- linux.orig/drivers/sound/pcsp_tables.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/sound/pcsp_tables.h Sun Dec 30 12:22:23 2001 @@ -0,0 +1,81 @@ +/* + * linux/drivers/sound/pcsp_tables.h + * + * /dev/pcsp implementation + * + * Copyright (C) 1993 Michael Beck + */ + +/* thanks to Mark J. Cox for his kindly permission + to use the following PC-Speaker table */ + +static unsigned char sp_tab[] = { + 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, 62, 62, + 61, 61, 61, 61, 61, 61, 61, 61, + 61, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 57, + 57, 57, 57, 57, 57, 57, 57, 57, + 57, 56, 56, 56, 56, 56, 56, 56, + 56, 55, 55, 55, 55, 55, 54, 54, + 54, 54, 53, 53, 53, 53, 52, 52, + 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 46, 45, 44, 43, 42, 41, + 40, 39, 38, 37, 36, 35, 34, 33, + 32, 31, 30, 29, 28, 27, 26, 25, + 24, 23, 22, 21, 20, 19, 18, 17, + 17, 16, 16, 15, 15, 14, 14, 13, + 13, 13, 12, 12, 12, 12, 11, 11, + 11, 11, 10, 10, 10, 10, 10, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, + 7, 7, 7, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 4, 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, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +}; + +static unsigned char ulaw[] = { + 3, 7, 11, 15, 19, 23, 27, 31, + 35, 39, 43, 47, 51, 55, 59, 63, + 66, 68, 70, 72, 74, 76, 78, 80, + 82, 84, 86, 88, 90, 92, 94, 96, + 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, + 113, 114, 114, 115, 115, 116, 116, 117, + 117, 118, 118, 119, 119, 120, 120, 121, + 121, 121, 122, 122, 122, 122, 123, 123, + 123, 123, 124, 124, 124, 124, 125, 125, + 125, 125, 125, 125, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 253, 249, 245, 241, 237, 233, 229, 225, + 221, 217, 213, 209, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 174, 172, 170, 168, 166, 164, 162, 160, + 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, + 143, 142, 142, 141, 141, 140, 140, 139, + 139, 138, 138, 137, 137, 136, 136, 135, + 135, 135, 134, 134, 134, 134, 133, 133, + 133, 133, 132, 132, 132, 132, 131, 131, + 131, 131, 131, 131, 130, 130, 130, 130, + 130, 130, 130, 130, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128 +}; + diff -urN linux.orig/include/linux/pcsp.h linux/include/linux/pcsp.h --- linux.orig/include/linux/pcsp.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/pcsp.h Sun Dec 30 12:21:59 2001 @@ -0,0 +1,135 @@ +#ifndef _LINUX_PCSP_H +#define _LINUX_PCSP_H +/* + * include/linux/pcsp.h + * + * /dev/pcsp implementation (dsp & audio for PC Speaker) + * + * Copyright (C) 1993-1997 Michael Beck + */ + +#include + +/* MUST BE defined from 0.5 */ +#define PCSP_SOUND_VERSION 0x103 /* read 1.03 */ + + +/* 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 +#define PCSP_SET_VOL 0x00054350 +#define PCSP_GET_VOL 0x00064350 +#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__) + +/* the timer stuff */ +#define TIMER_IRQ 0 + +/* the maximal samplerange for PC-Speaker: 18357 Hz */ +#define MIN_CONST 65 + +#define MAX_SRATE (CLOCK_TICK_RATE / MIN_CONST) +#define SRATE (CLOCK_TICK_RATE / pcsp.realrate) + +/* + * the default blocksize for playing thru + * PC-Speaker, STO1 and STNC use twice as much + */ + +#ifndef ABLK_SIZE +#define ABLK_SIZE 16368 +#endif + +/* 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 { + int last_clocks; + unsigned char *buf[2]; /* double buffering */ + unsigned char *buffer; + unsigned char *end; + unsigned in[2]; /* buffers fill */ + unsigned xfer; + unsigned index; + unsigned volume; /* volume for pc-speaker */ + unsigned left; /* left volume */ + unsigned right; /* right volume */ + 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 */ + unsigned actual; /* actual buffer */ + unsigned realrate; /* the real sample rate */ + 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; + char timer_on; + char mode; /* Mono / Stereo */ + char is_ulaw; /* need ULAW->LINEAR */ + 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_active, 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_device_init(void); +extern int pcsp_mixer_init(void); + +#endif +#endif