[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#127755: marked as done (RFP: kernel-patch-2.4-pcsnd -- kernel patch for PC speaker audio)



Your message dated Wed, 14 Sep 2005 21:55:04 -0500 (CDT)
with message-id <20050915025504.6BF1A1078B1@cerdita.damog.net>
and subject line WNPP bug closed
has caused the attached Bug report to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what I am
talking about this indicates a serious mail system misconfiguration
somewhere.  Please contact me immediately.)

Debian bug tracking system administrator
(administrator, Debian Bugs database)

--------------------------------------
Received: (at submit) by bugs.debian.org; 4 Jan 2002 11:09:07 +0000
>From debian-bugs-xp.tB=fM56Bt=spam@ruediger-kuhlmann.de Fri Jan 04 05:09:07 2002
Return-path: <debian-bugs-xp.tB=fM56Bt=spam@ruediger-kuhlmann.de>
Received: from mailgate.rz.uni-karlsruhe.de [129.13.64.97] 
	by master.debian.org with esmtp (Exim 3.12 1 (Debian))
	id 16MSDi-00081J-00; Fri, 04 Jan 2002 05:09:06 -0600
Received: from wh36-a402.stud.uni-karlsruhe.de (wh36-a402.stud.uni-karlsruhe.de [172.20.230.85])
	by mailgate.rz.uni-karlsruhe.de with esmtp (Exim 3.33 #1)
	id 16MSDD-0007Ba-00; Fri, 04 Jan 2002 12:08:35 +0100
Received: from ruediger by wh36-a402.stud.uni-karlsruhe.de with local (Exim 3.33 #1 (Debian))
	id 16MSBl-0001jd-00; Fri, 04 Jan 2002 12:07:05 +0100
Date: Fri, 4 Jan 2002 12:07:05 +0100
From: =?iso-8859-1?Q?R=FCdiger?= Kuhlmann <debian-bugs-xp.tB=fM56Bt=spam@ruediger-kuhlmann.de>
To: submit@bugs.debian.org
Subject: RFP: kernel-patch-2.4-pcsnd
Message-ID: <20020104110705.GB1007@wh36-a402.stud.uni-karlsruhe.de>
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="YZ5djTAD1cGYuMQK"
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
User-Agent: Mutt/1.3.25i
Delivered-To: submit@bugs.debian.org


--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Package: wnpp
Severity: wishlist

Please adopt the pcsnd.o PC speaker sound driver for 2.4. It is available at
ftp://ftp.uk.linux.org/pub/people/dwmw2/pcsp/; I tried to adopted the patch
for 2.4 and it seems to work fine, but maybe some kernel hacker should look
over it.

An ALSA version would be nice as well...

Also, if someone could tell me how to autoload it, let me know.

--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename="pcsp-2.4.16.gz"
Content-Transfer-Encoding: 8bit

diff -u -r -N kernel-source-2.4.16-orig/CREDITS kernel-source-2.4.16/CREDITS
--- kernel-source-2.4.16-orig/CREDITS	Sun Nov 11 19:09:32 2001
+++ kernel-source-2.4.16/CREDITS	Mon Dec 17 22:00:28 2001
@@ -363,6 +363,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
diff -u -r -N kernel-source-2.4.16-orig/Documentation/Configure.help kernel-source-2.4.16/Documentation/Configure.help
--- kernel-source-2.4.16-orig/Documentation/Configure.help	Sat Nov 24 03:34:20 2001
+++ kernel-source-2.4.16/Documentation/Configure.help	Mon Dec 17 22:04:55 2001
@@ -17826,11 +17826,6 @@
   as <file:Documentation/sound/README.modules>; 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 <ftp://ftp.infradead.org/pub/pcsp/>.
-
 OSS sound modules
 CONFIG_SOUND_OSS
   OSS is the Open Sound System suite of sound card drivers.  They make
@@ -18608,6 +18603,75 @@
 CONFIG_SOUND_RME96XX
   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
diff -u -r -N kernel-source-2.4.16-orig/Makefile kernel-source-2.4.16/Makefile
--- kernel-source-2.4.16-orig/Makefile	Tue Nov 27 21:29:41 2001
+++ kernel-source-2.4.16/Makefile	Mon Dec 17 22:02:00 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 -u -r -N kernel-source-2.4.16-orig/arch/i386/kernel/irq.c kernel-source-2.4.16/arch/i386/kernel/irq.c
--- kernel-source-2.4.16-orig/arch/i386/kernel/irq.c	Thu Oct 25 22:53:46 2001
+++ kernel-source-2.4.16/arch/i386/kernel/irq.c	Mon Dec 17 22:00:30 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 -u -r -N kernel-source-2.4.16-orig/arch/i386/kernel/time.c kernel-source-2.4.16/arch/i386/kernel/time.c
--- kernel-source-2.4.16-orig/arch/i386/kernel/time.c	Sun Nov 11 19:20:21 2001
+++ kernel-source-2.4.16/arch/i386/kernel/time.c	Mon Dec 17 22:00: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 -u -r -N kernel-source-2.4.16-orig/drivers/char/joystick/analog.c kernel-source-2.4.16/drivers/char/joystick/analog.c
--- kernel-source-2.4.16-orig/drivers/char/joystick/analog.c	Thu Oct 25 23:01:51 2001
+++ kernel-source-2.4.16/drivers/char/joystick/analog.c	Mon Dec 17 22:07:22 2001
@@ -39,6 +39,7 @@
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/gameport.h>
+#include <linux/pcsp.h>
 #include <asm/timex.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
@@ -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 -u -r -N kernel-source-2.4.16-orig/drivers/char/joystick/gameport.c kernel-source-2.4.16/drivers/char/joystick/gameport.c
--- kernel-source-2.4.16-orig/drivers/char/joystick/gameport.c	Fri Sep 14 23:40:00 2001
+++ kernel-source-2.4.16/drivers/char/joystick/gameport.c	Mon Dec 17 22:00:30 2001
@@ -39,6 +39,7 @@
 #include <linux/isapnp.h>
 #include <linux/stddef.h>
 #include <linux/delay.h>
+#include <linux/pcsp.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 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 -u -r -N kernel-source-2.4.16-orig/drivers/char/vt.c kernel-source-2.4.16/drivers/char/vt.c
--- kernel-source-2.4.16-orig/drivers/char/vt.c	Fri Nov 16 19:08:28 2001
+++ kernel-source-2.4.16/drivers/char/vt.c	Mon Dec 17 22:00:30 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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/Config.in kernel-source-2.4.16/drivers/sound/Config.in
--- kernel-source-2.4.16-orig/drivers/sound/Config.in	Mon Sep 24 13:24:33 2001
+++ kernel-source-2.4.16/drivers/sound/Config.in	Mon Dec 17 22:08:42 2001
@@ -8,6 +8,23 @@
 
 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 [ "$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
+
 if [ "$CONFIG_SOUND_CMPCI" = "y" -o "$CONFIG_SOUND_CMPCI" = "m" ]; then
     bool '    Enable legacy FM' CONFIG_SOUND_CMPCI_FM
     if [ "$CONFIG_SOUND_CMPCI_FM" = "y" ]; then
diff -u -r -N kernel-source-2.4.16-orig/drivers/sound/Makefile kernel-source-2.4.16/drivers/sound/Makefile
--- kernel-source-2.4.16-orig/drivers/sound/Makefile	Mon Sep 24 13:24:33 2001
+++ kernel-source-2.4.16/drivers/sound/Makefile	Mon Dec 17 22:11:31 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.
 
@@ -69,6 +69,10 @@
 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
+ifeq ($(CONFIG_PCSP),m)
+  obj-y += pcsp_stub.o
+endif
 obj-$(CONFIG_SOUND_EMU10K1)	+= ac97_codec.o
 obj-$(CONFIG_SOUND_RME96XX)     += rme96xx.o
 obj-$(CONFIG_SOUND_BT878)	+= btaudio.o
@@ -99,7 +103,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		\
@@ -115,6 +119,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
@@ -148,6 +153,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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/pcsndriv.c kernel-source-2.4.16/drivers/sound/pcsndriv.c
--- kernel-source-2.4.16-orig/drivers/sound/pcsndriv.c	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/drivers/sound/pcsndriv.c	Mon Dec 17 22:00:30 2001
@@ -0,0 +1,1021 @@
+/*
+ * linux/drivers/sound/pcsndriv.c
+ *
+ * /dev/pcsp implementation
+ *
+ * Copyright (C) 1993-1997  Michael Beck 
+ */
+
+#include <linux/config.h>
+#include <linux/soundcard.h>
+#include <linux/pcsp.h>
+ 
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timex.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#ifdef MODULE
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/pcsp.c kernel-source-2.4.16/drivers/sound/pcsp.c
--- kernel-source-2.4.16-orig/drivers/sound/pcsp.c	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/drivers/sound/pcsp.c	Mon Dec 17 22:00:30 2001
@@ -0,0 +1,90 @@
+/*
+ * linux/drivers/sound/pcsp.c
+ *
+ * /dev/pcsp implementation
+ *
+ * Copyright (C) 1993-1997  Michael Beck 
+ */
+
+#include <linux/config.h>
+#include <linux/soundcard.h>
+#include <linux/pcsp.h>
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/sound.h>
+
+MODULE_AUTHOR("Michael Beck <beck@DResearch.de>");
+MODULE_DESCRIPTION("Alternate Sound Driver for Linux 2.x Version 1.3");
+MODULE_SUPPORTED_DEVICE("pcsp");
+EXPORT_NO_SYMBOLS;
+#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;
+        }
+	
+	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_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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/pcsp_mixer.c kernel-source-2.4.16/drivers/sound/pcsp_mixer.c
--- kernel-source-2.4.16-orig/drivers/sound/pcsp_mixer.c	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/drivers/sound/pcsp_mixer.c	Mon Dec 17 22:00:30 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 <linux/config.h>
+
+#if defined(CONFIG_PCSP_16BIT) || defined(CONFIG_PCSP_MIXER)
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/soundcard.h>
+#include <linux/pcsp.h>
+#include <asm/uaccess.h>
+
+#ifdef MODULE
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/version.h>
+#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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/pcsp_stub.c kernel-source-2.4.16/drivers/sound/pcsp_stub.c
--- kernel-source-2.4.16-orig/drivers/sound/pcsp_stub.c	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/drivers/sound/pcsp_stub.c	Mon Dec 17 22:00:30 2001
@@ -0,0 +1,322 @@
+/*
+ * 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 <linux/config.h>
+#include <linux/pcsp.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/timex.h>
+#ifndef CONFIG_PCSP_MODULE
+#include <linux/sound.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+/*
+ * 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);
+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 -u -r -N kernel-source-2.4.16-orig/drivers/sound/pcsp_tables.h kernel-source-2.4.16/drivers/sound/pcsp_tables.h
--- kernel-source-2.4.16-orig/drivers/sound/pcsp_tables.h	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/drivers/sound/pcsp_tables.h	Mon Dec 17 22:00:30 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 -u -r -N kernel-source-2.4.16-orig/include/linux/pcsp.h kernel-source-2.4.16/include/linux/pcsp.h
--- kernel-source-2.4.16-orig/include/linux/pcsp.h	Thu Jan  1 01:00:00 1970
+++ kernel-source-2.4.16/include/linux/pcsp.h	Mon Dec 17 22:00:30 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 <linux/ioctl.h>
+
+/* 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

--YZ5djTAD1cGYuMQK--

---------------------------------------
Received: (at 127755-done) by bugs.debian.org; 15 Sep 2005 02:55:16 +0000
>From damog@cerdita.damog.net Wed Sep 14 19:55:15 2005
Return-path: <damog@cerdita.damog.net>
Received: from dsl-201-129-37-187.prod-infinitum.com.mx (cerdita.damog.net) [201.129.37.187] 
	by spohr.debian.org with esmtp (Exim 3.36 1 (Debian))
	id 1EFju7-0000W5-00; Wed, 14 Sep 2005 19:55:15 -0700
Received: by cerdita.damog.net (Postfix, from userid 1000)
	id 6BF1A1078B1; Wed, 14 Sep 2005 21:55:04 -0500 (CDT)
To: 127755-done@bugs.debian.org
Subject: WNPP bug closed
Message-Id: <20050915025504.6BF1A1078B1@cerdita.damog.net>
Date: Wed, 14 Sep 2005 21:55:04 -0500 (CDT)
From: damog@cerdita.damog.net (David Moreno Garza)
Delivered-To: 127755-done@bugs.debian.org
X-Spam-Checker-Version: SpamAssassin 2.60-bugs.debian.org_2005_01_02 
	(1.212-2003-09-23-exp) on spohr.debian.org
X-Spam-Level: 
X-Spam-Status: No, hits=-5.0 required=4.0 tests=BAYES_00,VALID_BTS_CONTROL 
	autolearn=no version=2.60-bugs.debian.org_2005_01_02
X-CrossAssassin-Score: 58

Hello,

This is an automatic mail sent to close the RFP you have reported or 
are involved with.

Your RFP wnpp bug is being closed because of the following reasons:
- It is, as of today, older than 600 days.
- It haven't had any activity recently.
- The amount of ITPs on the Debian BTS is huge and we need to
  clean up a bit the place.

As this an automatic procedure, it could of course have something
wrong and probably it would be closing some bugs that are not 
intended by owners and submitters (like you) to be closed, for
example if the RFP is still of your interest, or there has been 
some kind of activity around it. In that case, please reopen the
bug, do it, DO IT NOW! (I don't want to be blamed because of
mass closing and not let people know that they can easily reopen
their bugs ;-).

To re-open it, you simply have to mail control@bugs.debian.org
with a body text like this:

reopen 123456
thanks bts

Replacing '123456' for the number of your RFP bug. The subject of the
mail is ignored. Or if you have any kind of problems when dealing with
the BTS, feel free to contact me and I'd be more than happy to help
you on this: <damog@debian.org>.

This is the first mass wnpp closing that will be done. The next close
will be done on inactive RFPs older than 450 days and finally, the
ones older than 365 days (an automatic script will close *inactive*
RFPs when they reach one year old).

A similar process is being applied to the ITP wnpp bugs in these
days.

Thanks for your cooperation,

 -- David Moreno Garza <damog@debian.org>  Wed, 14 Sep 2005 21:54:42 -0500



Reply to: