Add support for injecting ACPI events

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

 



I am building a bare-bones CentOS 6.4 i386 server for remote data acquisition.  I am configuring as many features that I can find for automated state-of-health monitoring: smartd (disks), mcelogd (processors), and acpid (temperature -- alas, no supply voltage that I can find) for hardware alarms, watchdog for system lockups.  I set them all up to e-mail me alert messages.  The mcelog package has a nice testing feature that can inject MCE "events", using the mce-inject kernel module.  I could not find a similar feature for testing acpid ACPI event handling.  So, I wrote the acpi-inject kernel module below.

acpi-inject creates write-only /proc/acpi/inject to inject ACPI events, using the same format they are read from /proc/acpi/event (with lots of sanity checks).  I have been reading statements on the web that acpid is going away, and maybe /proc/acpi/event as well.  Even if this is a dead end, maybe one of the ACPI maintainers can pick up this idea and create an ACPI event injector that better fits the future ACPI framework.

FYI.  acpi-inject calls acpi_bus_generate_proc_event4() in acpi/bus.c to add an ACPI event to the queue.  I noticed there that the device_class and bus_id fields in the struct acpi_bus_event event are set using strcpy().  I think those should be strncpy()'s.  Also, I don't understand why the numeric arguments to acpi_bus_generate_proc_event4() are u8 and int, yet the corresponding fields in struct acpi_bus_event are u32.  No matter; just curious.

(If anyone knows how to monitor supply voltage, please let me know.  The system I am using is a tiny Intel Atom Z510-based system, and does not have an IPMI/BMC chip, as far as I can tell.)

Thank you,

Larry Baker
US Geological Survey
650-329-5608
baker@xxxxxxxx

--- linux-2.6.32-358.6.2.el6/drivers/acpi/Makefile
+++ linux-2.6.32-358.6.2.el6/drivers/acpi/Makefile
@@ -61,2 +61,3 @@
 obj-$(CONFIG_ACPI_HED)		+= hed.o
+obj-$(CONFIG_ACPI_INJECT)	+= acpi-inject.o
 
--- linux-2.6.32-358.6.2.el6/drivers/acpi/acpi-inject.c
+++ linux-2.6.32-358.6.2.el6/drivers/acpi/acpi-inject.c
@@ -0,0 +1,184 @@
+/*
+ * acpi-inject.c - Kernel module for ACPI event injection support.
+ *
+ * Creates write-only /proc/acpi/inject, which accepts ACPI event messages in
+ * the format they are read from /proc/acpi/event (acpi_system_read_event()
+ * in event.c):
+ *
+ *    "%s %s %08x %08x"
+ *
+ * There must be exactly four fields, separated by whitespace.  Leading and
+ * trailing whitespace is ignored.  The fields are char device_name[40],
+ * char bus_id[8], u8 type, and int data.  The (unsigned) hex fields must
+ * be (case insensitive) hex digits only.
+ *
+ * Usage:
+ *
+ *    # modprobe acpi-inject
+ *    # service acpid start
+ *    # echo "thermal_zone TZ1 00000081 00000000" >/proc/acpi/inject
+ *
+ *    acpid should receive the Thermal Zone event and process it according
+ *    to the rules in /etc/acpi/events/.
+ *
+ * Author:
+ * Larry Baker
+ */
+
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#ifndef UCHAR_MAX
+#define UCHAR_MAX       ((u8)(~0U))
+#endif
+
+#define _COMPONENT		ACPI_SYSTEM_COMPONENT
+ACPI_MODULE_NAME("inject");
+
+#ifdef CONFIG_ACPI_PROC_EVENT
+
+/* Global vars for handling inject proc entry */
+static DEFINE_SPINLOCK(acpi_system_inject_lock);
+int inject_is_open = 0;
+
+static int acpi_system_open_inject(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&acpi_system_inject_lock);
+
+	if (inject_is_open)
+		goto out_busy;
+
+	inject_is_open = 1;
+
+	spin_unlock_irq(&acpi_system_inject_lock);
+	return 0;
+
+out_busy:
+	spin_unlock_irq(&acpi_system_inject_lock);
+	return -EBUSY;
+}
+
+/* ACPI event record format: "%s %s %08x %08x" */
+
+static ssize_t
+acpi_system_write_inject(struct file *file, const char __user * buffer,
+			 size_t count, loff_t * ppos)
+{
+	static char str[ACPI_MAX_STRING];
+	char *ptr;
+	char *device_class = NULL, *bus_id = NULL, *typeP = NULL, *dataP = NULL;
+	unsigned long hex;
+	u8 type;
+	int data;
+	int i = 0, err;
+
+	if (count > sizeof(str) - 1)
+		return -EINVAL;
+
+	if (copy_from_user(str, buffer, count))
+		return -EFAULT;
+
+	str[count] = '\0';
+
+	ptr = str;
+	do {
+		while (isspace(*ptr))
+			ptr++;
+		if (*ptr) {
+			switch (i++) {
+				case 0:
+					device_class = ptr;
+					break;
+				case 1:
+					bus_id = ptr;
+					break;
+				case 2:
+					typeP = ptr;
+					break;
+				case 3:
+					dataP = ptr;
+					break;
+				default:
+					return -EINVAL;
+			}
+			while (*ptr && !isspace(*ptr))
+				ptr++;
+			if (*ptr)
+				*ptr++ = '\0';
+		}
+	} while (*ptr);
+
+	if (i != 4)
+		return -EINVAL;
+
+	if (strlen(device_class) > sizeof(acpi_device_class) - 1)
+		return -EINVAL;
+
+	if (strlen(bus_id ) > sizeof(acpi_bus_id) - 1)
+		return -EINVAL;
+
+	err = kstrtoul(typeP, 16, &hex);
+	if (err)
+		return err;
+	if (hex > UCHAR_MAX)
+		return -ERANGE;
+	type = hex;
+
+	err = kstrtoul(dataP, 16, &hex);
+	if (err)
+		return err;
+	if (hex > INT_MAX)
+		return -ERANGE;
+	data = hex;
+
+	acpi_bus_generate_proc_event4(device_class, bus_id, type, data);
+
+	return count;
+}
+
+static int acpi_system_close_inject(struct inode *inode, struct file *file)
+{
+	spin_lock_irq(&acpi_system_inject_lock);
+	inject_is_open = 0;
+	spin_unlock_irq(&acpi_system_inject_lock);
+	return 0;
+}
+
+static const struct file_operations acpi_system_inject_ops = {
+	.owner = THIS_MODULE,
+	.open = acpi_system_open_inject,
+	.write = acpi_system_write_inject,
+	.release = acpi_system_close_inject,
+};
+
+#endif	/* CONFIG_ACPI_PROC_EVENT */
+
+static int __init acpi_inject_init(void)
+{
+#ifdef CONFIG_ACPI_PROC_EVENT
+	struct proc_dir_entry *entry;
+#endif
+	if (acpi_disabled)
+		return 0;
+
+#ifdef CONFIG_ACPI_PROC_EVENT
+	/* 'inject' [W] */
+	entry = proc_create("inject", S_IWUSR, acpi_root_dir,
+		&acpi_system_inject_ops);
+	if (!entry)
+		return -ENODEV;
+#endif
+
+	return 0;
+}
+
+fs_initcall(acpi_inject_init);
+
+MODULE_LICENSE("GPL");

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux