From: Stas Sergeev <stsp@aknet.ru>

Added SND_SILENT event that allows to disable (and enable) the driver,
either to grant the hardware access to some other driver, or to disable the
annoying beeps.

Signed-off-by: Stas Sergeev <stsp@aknet.ru>
Cc: Vojtech Pavlik <vojtech@suse.cz>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 drivers/input/misc/pcspkr.c |   62 +++++++++++++++++++++++++++++++-------------
 include/linux/input.h       |    1 
 2 files changed, 46 insertions(+), 17 deletions(-)

diff -puN drivers/input/misc/pcspkr.c~pc-speaker-add-snd_silent drivers/input/misc/pcspkr.c
--- devel/drivers/input/misc/pcspkr.c~pc-speaker-add-snd_silent	2005-09-13 18:47:45.000000000 -0700
+++ devel-akpm/drivers/input/misc/pcspkr.c	2005-09-13 18:47:45.000000000 -0700
@@ -27,25 +27,16 @@ static char pcspkr_name[] = "PC Speaker"
 static char pcspkr_phys[] = "isa0061/input0";
 static struct input_dev pcspkr_dev;
 
+/* Please replace this with i8253_lock to properly serialize an
+ * accesses to port 0x43 */
 static DEFINE_SPINLOCK(i8253_beep_lock);
 
-static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+enum { PCSPKR_NORMAL, PCSPKR_SUSPENDED };
+
+static void pcspkr_do_sound(unsigned int count)
 {
-	unsigned int count = 0;
 	unsigned long flags;
 
-	if (type != EV_SND)
-		return -1;
-
-	switch (code) {
-		case SND_BELL: if (value) value = 1000;
-		case SND_TONE: break;
-		default: return -1;
-	}
-
-	if (value > 20 && value < 32767)
-		count = PIT_TICK_RATE / value;
-
 	spin_lock_irqsave(&i8253_beep_lock, flags);
 
 	if (count) {
@@ -62,6 +53,40 @@ static int pcspkr_event(struct input_dev
 	}
 
 	spin_unlock_irqrestore(&i8253_beep_lock, flags);
+}
+
+static int pcspkr_event(struct input_dev *dev, unsigned int type,
+		unsigned int code, int value)
+{
+	unsigned int count = 0;
+
+	switch (type) {
+	case EV_SND:
+		switch (code) {
+		case SND_BELL:
+			if (value)
+				value = 1000;
+		case SND_TONE:
+			break;
+		case SND_SILENT:
+			dev->state = value ? PCSPKR_SUSPENDED : PCSPKR_NORMAL;
+			return 0;
+		default:
+			return -1;
+		}
+		break;
+
+	default:
+		return -1;
+	}
+
+	if (dev->state == PCSPKR_SUSPENDED)
+		return 0;
+
+	if (value > 20 && value < 32767)
+		count = PIT_TICK_RATE / value;
+
+	pcspkr_do_sound(count);
 
 	return 0;
 }
@@ -69,7 +94,7 @@ static int pcspkr_event(struct input_dev
 static int __init pcspkr_init(void)
 {
 	pcspkr_dev.evbit[0] = BIT(EV_SND);
-	pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
+	pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE) | BIT(SND_SILENT);
 	pcspkr_dev.event = pcspkr_event;
 
 	pcspkr_dev.name = pcspkr_name;
@@ -78,6 +103,7 @@ static int __init pcspkr_init(void)
 	pcspkr_dev.id.vendor = 0x001f;
 	pcspkr_dev.id.product = 0x0001;
 	pcspkr_dev.id.version = 0x0100;
+	pcspkr_dev.state = PCSPKR_NORMAL;
 
 	input_register_device(&pcspkr_dev);
 
@@ -88,9 +114,11 @@ static int __init pcspkr_init(void)
 
 static void __exit pcspkr_exit(void)
 {
+	if (pcspkr_dev.state == PCSPKR_NORMAL) {
+		/* turn off the speaker */
+		pcspkr_do_sound(0);
+	}
         input_unregister_device(&pcspkr_dev);
-	/* turn off the speaker */
-	pcspkr_event(NULL, EV_SND, SND_BELL, 0);
 }
 
 module_init(pcspkr_init);
diff -puN include/linux/input.h~pc-speaker-add-snd_silent include/linux/input.h
--- devel/include/linux/input.h~pc-speaker-add-snd_silent	2005-09-13 18:47:45.000000000 -0700
+++ devel-akpm/include/linux/input.h	2005-09-13 18:47:45.000000000 -0700
@@ -617,6 +617,7 @@ struct input_absinfo {
 #define SND_CLICK		0x00
 #define SND_BELL		0x01
 #define SND_TONE		0x02
+#define SND_SILENT		0x06
 #define SND_MAX			0x07
 
 /*
_