diff --new-file -u --recursive --exclude *~ linux-2.4.4-orig/arch/i386/kernel/apm.c linux-2.4.4-pmevent/arch/i386/kernel/apm.c
--- linux-2.4.4-orig/arch/i386/kernel/apm.c	Tue May  1 14:33:34 2001
+++ linux-2.4.4-pmevent/arch/i386/kernel/apm.c	Wed May  2 13:46:08 2001
@@ -148,6 +148,10 @@
  *   1.14: Make connection version persist across module unload/load.
  *         Enable and engage power management earlier.
  *         Disengage power management on module unload.
+ *   1.15j: Reject all rejectable APM events by default (so
+ *          userspace can decide whether to suspend the machine).
+ *          Minor waitqueue cleanups.
+ *          (John Fremlin <chief@bandits.org>)
  *
  * APM 1.1 Reference:
  *
@@ -186,6 +190,7 @@
 #include <linux/pm.h>
 #include <linux/kernel.h>
 #include <linux/smp_lock.h>
+#include <linux/pmevent.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -327,7 +332,6 @@
 #endif
 static int			suspends_pending;
 static int			standbys_pending;
-static int			waiting_for_resume;
 static int			ignore_normal_resume;
 static int			bounce_interval = DEFAULT_BOUNCE_INTERVAL;
 
@@ -352,7 +356,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 static struct apm_user *	user_list;
 
-static char			driver_version[] = "1.14";	/* no spaces */
+static char			driver_version[] = "1.15";	/* no spaces */
 
 static char *	apm_event_name[] = {
 	"system standby",
@@ -886,33 +890,6 @@
 #endif
 }
 
-static int send_event(apm_event_t event)
-{
-	switch (event) {
-	case APM_SYS_SUSPEND:
-	case APM_CRITICAL_SUSPEND:
-	case APM_USER_SUSPEND:
-		/* map all suspends to ACPI D3 */
-		if (pm_send_all(PM_SUSPEND, (void *)3)) {
-			if (event == APM_CRITICAL_SUSPEND) {
-				printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armageddon\n" );
-				return 0;
-			}
-			if (apm_info.connection_version > 0x100)
-				apm_set_power_state(APM_STATE_REJECT);
-			return 0;
-		}
-		break;
-	case APM_NORMAL_RESUME:
-	case APM_CRITICAL_RESUME:
-		/* map all resumes to ACPI D0 */
-		(void) pm_send_all(PM_RESUME, (void *)0);
-		break;
-	}
-
-	return 1;
-}
-
 static int suspend(void)
 {
 	int		err;
@@ -927,7 +904,6 @@
 		err = APM_SUCCESS;
 	if (err != APM_SUCCESS)
 		apm_error("suspend", err);
-	send_event(APM_NORMAL_RESUME);
 	sti();
 	queue_event(APM_NORMAL_RESUME, NULL);
 	for (as = user_list; as != NULL; as = as->next) {
@@ -968,6 +944,54 @@
 	return 0;
 }
 
+static int may_sleep(void) 
+{
+	/* map all suspends to ACPI D3 */
+	return (!pm_send_all(PM_SUSPEND, (void *)3));
+}
+static int resume_all(void) 
+{
+	/* map all resumes to ACPI D0 */
+	(void) pm_send_all(PM_RESUME, (void *)0);
+}
+
+static void announce_event(apm_event_t event)
+{
+	char*type = PME_NOTIFY;
+	char*name;
+
+	if (event <= NR_APM_EVENT_NAME)
+		name = apm_event_name[event - 1];
+	else	name = "unknown event";
+	
+	switch(event){
+	case APM_SYS_STANDBY:
+	case APM_USER_STANDBY:
+	case APM_SYS_SUSPEND:
+	case APM_USER_SUSPEND:
+	case APM_CRITICAL_SUSPEND:
+		type = PME_SLEEP;
+		break;
+	case APM_NORMAL_RESUME:
+	case APM_CRITICAL_RESUME:
+	case APM_STANDBY_RESUME:
+		type = PME_WAKE;
+		break;
+	case APM_CAPABILITY_CHANGE:
+		type = PME_NOTIFY;
+		break;
+	case APM_LOW_BATTERY:
+		type = PME_EMERGENCY;
+		break;
+	case APM_POWER_STATUS_CHANGE:
+		type = PME_POWERCHANGE;
+		break;
+	default:
+		type = PME_NOTIFY;
+	}
+	pme_announce(type,"APM",name);
+}
+
 static void check_events(void)
 {
 	apm_event_t		event;
@@ -989,56 +1013,36 @@
 		if (ignore_normal_resume && (event != APM_NORMAL_RESUME))
 			ignore_normal_resume = 0;
 
+		announce_event(event);
+		
 		switch (event) {
 		case APM_SYS_STANDBY:
 		case APM_USER_STANDBY:
-			if (send_event(event)) {
-				queue_event(event, NULL);
-				if (standbys_pending <= 0)
-					standby();
-			}
+			queue_event(event, NULL);
+			if (standbys_pending <= 0)
+				standby();
 			break;
 
 		case APM_USER_SUSPEND:
-#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
-			if (apm_info.connection_version > 0x100)
-				apm_set_power_state(APM_STATE_REJECT);
-			break;
-#endif
 		case APM_SYS_SUSPEND:
-			if (ignore_bounce) {
-				if (apm_info.connection_version > 0x100)
-					apm_set_power_state(APM_STATE_REJECT);
-				break;
-			}
-			/*
-			 * If we are already processing a SUSPEND,
-			 * then further SUSPEND events from the BIOS
-			 * will be ignored.  We also return here to
-			 * cope with the fact that the Thinkpads keep
-			 * sending a SUSPEND event until something else
-			 * happens!
+			/* Reject all suspend requests and let
+			 * userspace call suspend if they want to
 			 */
-			if (waiting_for_resume)
-				return;
-			if (send_event(event)) {
-				queue_event(event, NULL);
-				waiting_for_resume = 1;
-				if (suspends_pending <= 0)
-					(void) suspend();
-			}
+			if (apm_info.connection_version > 0x100)
+				apm_set_power_state(APM_STATE_REJECT);
+
+			queue_event(event, NULL);
 			break;
 
 		case APM_NORMAL_RESUME:
 		case APM_CRITICAL_RESUME:
 		case APM_STANDBY_RESUME:
-			waiting_for_resume = 0;
 			last_resume = jiffies;
 			ignore_bounce = 1;
+			resume_all();
 			if ((event != APM_NORMAL_RESUME)
 			    || (ignore_normal_resume == 0)) {
 				set_time();
-				send_event(event);
 				queue_event(event, NULL);
 			}
 			break;
@@ -1046,7 +1050,6 @@
 		case APM_CAPABILITY_CHANGE:
 		case APM_LOW_BATTERY:
 		case APM_POWER_STATUS_CHANGE:
-			send_event(event);
 			queue_event(event, NULL);
 			break;
 
@@ -1055,12 +1058,17 @@
 			break;
 
 		case APM_CRITICAL_SUSPEND:
-			send_event(event);
 			/*
-			 * We can only hope it worked - we are not allowed
+			 * We are not allowed
 			 * to reject a critical suspend.
 			 */
-			(void) suspend();
+			if(!may_sleep()){
+				printk(KERN_CRIT "apm: critical suspend was vetoed, expect armageddon\n");
+				if (apm_info.connection_version > 0x100)
+					apm_set_power_state(APM_STATE_REJECT);
+			}
+			else	
+				(void) suspend();
 			break;
 		}
 	}
@@ -1151,7 +1159,9 @@
 	struct apm_user *	as;
 	int			i;
 	apm_event_t		event;
-	DECLARE_WAITQUEUE(wait, current);
+
+	if (ppos != &fp->f_pos)
+		return -ESPIPE;
 
 	as = fp->private_data;
 	if (check_apm_user(as, "read"))
@@ -1161,15 +1171,7 @@
 	if (queue_empty(as)) {
 		if (fp->f_flags & O_NONBLOCK)
 			return -EAGAIN;
-		add_wait_queue(&apm_waitqueue, &wait);
-repeat:
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (queue_empty(as) && !signal_pending(current)) {
-			schedule();
-			goto repeat;
-		}
-		set_current_state(TASK_RUNNING);
-		remove_wait_queue(&apm_waitqueue, &wait);
+		wait_event_interruptible(apm_waitqueue,!queue_empty(as));
 	}
 	i = count;
 	while ((i >= sizeof(event)) && !queue_empty(as)) {
@@ -1217,7 +1219,6 @@
 		    u_int cmd, u_long arg)
 {
 	struct apm_user *	as;
-	DECLARE_WAITQUEUE(wait, current);
 
 	as = filp->private_data;
 	if (check_apm_user(as, "ioctl"))
@@ -1230,7 +1231,7 @@
 			as->standbys_read--;
 			as->standbys_pending--;
 			standbys_pending--;
-		} else if (!send_event(APM_USER_STANDBY))
+		} else if (!may_sleep())
 			return -EAGAIN;
 		else
 			queue_event(APM_USER_STANDBY, as);
@@ -1242,7 +1243,7 @@
 			as->suspends_read--;
 			as->suspends_pending--;
 			suspends_pending--;
-		} else if (!send_event(APM_USER_SUSPEND))
+		} else if (!may_sleep())
 			return -EAGAIN;
 		else
 			queue_event(APM_USER_SUSPEND, as);
@@ -1251,16 +1252,8 @@
 				return -EIO;
 		} else {
 			as->suspend_wait = 1;
-			add_wait_queue(&apm_suspend_waitqueue, &wait);
-			while (1) {
-				set_current_state(TASK_INTERRUPTIBLE);
-				if ((as->suspend_wait == 0)
-				    || signal_pending(current))
-					break;
-				schedule();
-			}
-			set_current_state(TASK_RUNNING);
-			remove_wait_queue(&apm_suspend_waitqueue, &wait);
+			wait_event_interruptible(apm_suspend_waitqueue,
+						 as->suspend_wait == 0);
 			return as->suspend_result;
 		}
 		break;
diff --new-file -u --recursive --exclude *~ linux-2.4.4-orig/include/linux/pmevent.h linux-2.4.4-pmevent/include/linux/pmevent.h
--- linux-2.4.4-orig/include/linux/pmevent.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.4-pmevent/include/linux/pmevent.h	Wed May  2 13:47:15 2001
@@ -0,0 +1,34 @@
+/* (C) 2001 John Fremlin */
+
+#ifndef _LINUX_PMEVENT_H_
+#define _LINUX_PMEVENT_H_
+
+#define PME_NOTIFY		"NOTIFY"
+#define PME_SLEEP		"SLEEP"
+#define PME_OFF			"OFF"
+#define PME_WAKE		"WAKE"
+#define PME_EMERGENCY		"EMERGENCY"
+#define PME_POWERCHANGE	        PME_NOTIFY
+
+#define PME_FLOOD		"MSGFLOOD"
+					/* only generated if a buffer
+					 * internal to the pm event
+					 * system overflows
+					 */
+#ifdef __KERNEL__
+#include <linux/config.h>
+
+#ifdef CONFIG_PM
+void pme_announce(const char* event_type,
+		  const char* subsystem,
+		  const char* desc);
+#else
+static inline void  pme_announce(const char* event_type,
+				 const char* subsystem,
+				 const char* desc) 
+{
+}
+#endif
+#endif
+
+#endif
diff --new-file -u --recursive --exclude *~ linux-2.4.4-orig/kernel/Makefile linux-2.4.4-pmevent/kernel/Makefile
--- linux-2.4.4-orig/kernel/Makefile	Tue Feb 20 16:45:51 2001
+++ linux-2.4.4-pmevent/kernel/Makefile	Tue May  1 18:01:49 2001
@@ -18,7 +18,7 @@
 
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += ksyms.o
-obj-$(CONFIG_PM) += pm.o
+obj-$(CONFIG_PM) += pm.o pmevent.o
 
 ifneq ($(CONFIG_IA64),y)
 # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --new-file -u --recursive --exclude *~ linux-2.4.4-orig/kernel/pmevent.c linux-2.4.4-pmevent/kernel/pmevent.c
--- linux-2.4.4-orig/kernel/pmevent.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.4-pmevent/kernel/pmevent.c	Wed May  2 13:47:52 2001
@@ -0,0 +1,264 @@
+/* (C) 2001 John Fremlin */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+#include <linux/pmevent.h>
+
+static const char msg_overflow[] = "MSGFLOOD KERNEL messages lost\n";
+
+#define	N_MSGS		16
+#define	MSG_MAXLEN	100
+
+static const char ring[N_MSGS][MSG_MAXLEN];
+static unsigned ring_next; /*=0*/
+
+struct fop_priv
+{
+	int fp_overflow;
+	unsigned fp_ringpos;
+	unsigned fp_msgpos;
+	
+	struct list_head fp_entry;
+}
+;
+
+static DECLARE_WAIT_QUEUE_HEAD(waiters);
+static LIST_HEAD(listeners);
+static spinlock_t listeners_lock = SPIN_LOCK_UNLOCKED;
+	/* protects listeners, ring, ring_next and all the ring_pos in the fop_priv */
+
+/**
+ *	pme_announce - inform userspace listeners of a power management event
+ *	@event_type: one of the PME_* defines in linux/pmevent.h, general event class
+ *	@subsystem: kernel subsystem generating the event (e.g. "APM")
+ *	@desc: longer description of event (e.g. "lid closed")
+ *
+ *	This function must not be called from interrupt context.
+ */
+
+void pme_announce(const char* event_type,
+		  const char* subsystem,
+		  const char* desc)
+{
+	unsigned ring_cur;
+	
+	/* No snprintf in kernel */
+	if(strlen(event_type)+strlen(subsystem)+strlen(desc) + 5 >=
+		MSG_MAXLEN) {
+		panic("pme_announce arguments too long\n");
+	}
+
+	printk(KERN_DEBUG "pm event: %s %s %s\n",event_type,subsystem,desc);
+	
+	spin_lock(&listeners_lock);
+
+	ring_cur = ring_next;
+	
+	if(++ring_next >= N_MSGS)
+		ring_next = 0;
+	
+	{
+		struct list_head *i;
+		
+		list_for_each(i,&listeners) {
+			struct fop_priv *priv = list_entry(i, struct fop_priv, fp_entry);
+			if(ring_next == priv->fp_ringpos) {
+				/* we will actually overflow next item,
+				 * but it is easier to complain now
+				 */
+				priv->fp_msgpos = 0;
+				priv->fp_overflow = 1;
+			}
+		}
+
+		
+	}
+
+	sprintf((char*)ring[ring_cur],"%s %s %s\n",event_type,subsystem,desc);
+	
+	spin_unlock(&listeners_lock);
+	
+	wake_up_all(&waiters); /* Recipe for contention */
+}
+
+static ssize_t do_read(struct fop_priv*priv,char*buf,size_t count)
+{
+	ssize_t ret = 0;
+	char* msg; 
+
+	if(!priv->fp_overflow) {
+		if(priv->fp_ringpos == ring_next) {
+			ret = ret ? ret : -EAGAIN;
+			goto out;
+		}
+		msg = (char*)ring[priv->fp_ringpos];
+	}
+	else
+		msg = (char*)msg_overflow;
+			
+	msg += priv->fp_msgpos;
+	ret = strlen(msg);
+	if(ret <= count){
+		if(priv->fp_overflow){
+			priv->fp_overflow = 0;
+			priv->fp_ringpos = ring_next;
+			/* This means the minimum number of
+			 * messages is lost, which might not
+			 * be an altogether good idea, because
+			 * the reader is not given much chance
+			 * to catch up with the flood
+			 */
+		}
+
+		if (++priv->fp_ringpos >= N_MSGS)
+			priv->fp_ringpos = 0;
+			
+		priv->fp_msgpos = 0;
+	} else {
+		ret = count;
+		priv->fp_msgpos += count;
+	}
+		
+	if (copy_to_user(buf, msg, ret))
+		ret = -EFAULT;
+
+ out:
+	return ret;
+}
+	
+
+static ssize_t fop_read(struct file * file, char * buf,
+			size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	struct fop_priv *priv = (struct fop_priv *)file->private_data;
+
+	if (ppos != &file->f_pos) {
+		ret = -ESPIPE;
+		goto out;
+	}
+	
+ retry:	
+	spin_lock(&listeners_lock);
+
+	ret = do_read(priv,buf,count);
+
+	spin_unlock(&listeners_lock);
+
+	if((ret == -EAGAIN) && !(file->f_flags&O_NONBLOCK)) {
+		if (wait_event_interruptible(waiters,
+		    priv->fp_overflow || priv->fp_ringpos != ring_next)
+		    == -ERESTARTSYS) {
+			ret = ret ? ret : -ERESTARTSYS;
+			goto out;
+		}
+		goto retry;
+	}
+ out:
+	return ret;
+}
+
+static int fop_open(struct inode * inode, struct file * file)
+{
+	struct fop_priv*priv;
+	priv = kmalloc(sizeof *priv,GFP_KERNEL);
+	if(!priv)
+		return -ENOMEM;
+
+	memset(priv,0,sizeof *priv);
+	
+	file->private_data = priv;
+
+	spin_lock(&listeners_lock);
+	priv->fp_ringpos = ring_next;
+	list_add(&priv->fp_entry, &listeners);
+	spin_unlock(&listeners_lock);
+
+	return 0;
+}
+
+static void do_remove_priv(struct fop_priv *priv)
+{
+	list_del(&priv->fp_entry);
+	kfree(priv);
+}
+
+static int fop_release(struct inode * inode, struct file * file)
+{
+	struct fop_priv *priv = (struct fop_priv *)file->private_data;
+
+	spin_lock(&listeners_lock);
+
+	do_remove_priv(priv);
+	
+	spin_unlock(&listeners_lock);
+
+	return 0;
+}
+
+static unsigned int fop_poll(struct file *file, poll_table * wait)
+{
+	struct fop_priv *priv = (struct fop_priv *)file->private_data;
+	unsigned ret = 0;
+	
+	poll_wait(file, &waiters, wait);
+
+	spin_lock(&listeners_lock);
+	if(priv->fp_overflow || priv->fp_ringpos != ring_next)
+		ret = POLLIN | POLLRDNORM;
+	spin_unlock(&listeners_lock);
+
+	return ret;
+}
+
+
+static struct file_operations pme_fops = {
+	read:		fop_read,
+	poll:		fop_poll,
+	open:		fop_open,
+	release:        fop_release,
+};
+
+static struct miscdevice pme_dev=
+{
+	PMEVENT_MINOR,
+	"pmevent",
+	&pme_fops
+};
+
+static int __init pme_init(void)
+{
+	misc_register(&pme_dev);
+	printk(KERN_INFO "pm event: subsystem ready\n");
+	return 0;
+}
+
+static void __exit pme_exit(void)
+{
+	struct list_head *i,*j;
+
+	misc_deregister(&pme_dev);
+	printk(KERN_INFO "pm event: subsystem closing\n");
+
+	spin_lock(&listeners_lock);
+
+	for(i=listeners.next;i!=&listeners;i=j){
+		struct fop_priv *priv
+			= list_entry(i, struct fop_priv, fp_entry);
+		j = i->next;
+		do_remove_priv(priv);
+	}
+}
+
+module_init(pme_init);
+module_exit(pme_exit);

