lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1457992472-18680-1-git-send-email-kyle.roeschley@ni.com>
Date:	Mon, 14 Mar 2016 16:54:32 -0500
From:	Kyle Roeschley <kyle.roeschley@...com>
To:	gregkh@...uxfoundation.org, arnd@...db.de
Cc:	linux-kernel@...r.kernel.org, jeff.westfahl@...com, joshc@...com,
	nathan.sullivan@...com, xander.huff@...com
Subject: [PATCH 1/2] misc: add nirtfeatures driver

From: Jeff Westfahl <jeff.westfahl@...com>

This driver introduces support for hardware features of National
Instruments real-time controllers. This is an ACPI device that exposes
LEDs, switches, and watchdogs.

Signed-off-by: Jeff Westfahl <jeff.westfahl@...com>
Signed-off-by: Kyle Roeschley <kyle.roeschley@...com>
---
 drivers/misc/Kconfig        |   9 +
 drivers/misc/Makefile       |   1 +
 drivers/misc/nirtfeatures.c | 575 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 585 insertions(+)
 create mode 100644 drivers/misc/nirtfeatures.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1557951..d97f71e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -526,6 +526,15 @@ config VEXPRESS_SYSCFG
 	  bus. System Configuration interface is one of the possible means
 	  of generating transactions on this bus.
 
+config NI_RT_FEATURES
+	bool "NI 903x/913x support"
+	depends on X86 && ACPI
+	help
+	  This driver exposes LEDs and other features of NI 903x/913x Real-Time
+	  controllers.
+
+	  If unsure, say N (but it's safe to say "Y").
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 537d7f3..539127d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE)		+= genwqe/
 obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
+obj-$(CONFIG_NI_RT_FEATURES)	+= nirtfeatures.o
diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c
new file mode 100644
index 0000000..37298f2
--- /dev/null
+++ b/drivers/misc/nirtfeatures.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2016 National Instruments Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#define MODULE_NAME "nirtfeatures"
+
+/* Register addresses */
+
+#define NIRTF_YEAR		0x01
+#define NIRTF_MONTH		0x02
+#define NIRTF_DAY		0x03
+#define NIRTF_HOUR		0x04
+#define NIRTF_MINUTE		0x05
+#define NIRTF_SCRATCH		0x06
+#define NIRTF_PLATFORM_MISC	0x07
+#define NIRTF_PROC_RESET_SOURCE	0x11
+#define NIRTF_CONTROLLER_MODE	0x12
+#define NIRTF_SYSTEM_LEDS	0x20
+#define NIRTF_STATUS_LED_SHIFT1	0x21
+#define NIRTF_STATUS_LED_SHIFT0	0x22
+#define NIRTF_RT_LEDS		0x23
+
+#define NIRTF_IO_SIZE		0x40
+
+/* Register values */
+
+#define NIRTF_PLATFORM_MISC_ID_MASK		0x07
+#define NIRTF_PLATFORM_MISC_ID_MANHATTAN	0
+#define NIRTF_PLATFORM_MISC_ID_HAMMERHEAD	4
+#define NIRTF_PLATFORM_MISC_ID_WINGHEAD		5
+
+#define NIRTF_CONTROLLER_MODE_NO_FPGA_SW	0x40
+#define NIRTF_CONTROLLER_MODE_HARD_BOOT_N	0x20
+#define NIRTF_CONTROLLER_MODE_NO_FPGA		0x10
+#define NIRTF_CONTROLLER_MODE_RECOVERY		0x08
+#define NIRTF_CONTROLLER_MODE_CONSOLE_OUT	0x04
+#define NIRTF_CONTROLLER_MODE_IP_RESET		0x02
+#define NIRTF_CONTROLLER_MODE_SAFE		0x01
+
+#define NIRTF_SYSTEM_LEDS_STATUS_RED		0x08
+#define NIRTF_SYSTEM_LEDS_STATUS_YELLOW		0x04
+#define NIRTF_SYSTEM_LEDS_POWER_GREEN		0x02
+#define NIRTF_SYSTEM_LEDS_POWER_YELLOW		0x01
+
+#define NIRTF_RT_LEDS_USER2_GREEN	0x08
+#define NIRTF_RT_LEDS_USER2_YELLOW	0x04
+#define NIRTF_RT_LEDS_USER1_GREEN	0x02
+#define NIRTF_RT_LEDS_USER1_YELLOW	0x01
+
+#define to_nirtfeatures(dev)	acpi_driver_data(to_acpi_device(dev))
+
+/* Structures */
+
+struct nirtfeatures {
+	struct acpi_device *acpi_device;
+	u16 io_base;
+	spinlock_t lock;
+	u8 revision[5];
+	const char *bpstring;
+	struct nirtfeatures_led *extra_leds;
+	unsigned num_extra_leds;
+};
+
+struct nirtfeatures_led {
+	struct led_classdev cdev;
+	struct nirtfeatures *nirtfeatures;
+	u8 address;
+	u8 mask;
+	u8 pattern_hi_addr;
+	u8 pattern_lo_addr;
+};
+
+/* sysfs files */
+
+static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+
+	return sprintf(buf, "20%02X/%02X/%02X %02X:%02X\n",
+		       nirtfeatures->revision[0], nirtfeatures->revision[1],
+		       nirtfeatures->revision[2], nirtfeatures->revision[3],
+		       nirtfeatures->revision[4]);
+}
+static DEVICE_ATTR_RO(revision);
+
+static ssize_t scratch_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	u8 data;
+
+	spin_lock(&nirtfeatures->lock);
+
+	data = inb(nirtfeatures->io_base + NIRTF_SCRATCH);
+
+	spin_unlock(&nirtfeatures->lock);
+
+	return sprintf(buf, "%02x\n", data);
+}
+
+static ssize_t scratch_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	unsigned long tmp;
+
+	if (kstrtoul(buf, 0, &tmp) || (tmp > 0xFF))
+		return -EINVAL;
+
+	spin_lock(&nirtfeatures->lock);
+
+	outb((u8)tmp, nirtfeatures->io_base + NIRTF_SCRATCH);
+
+	spin_unlock(&nirtfeatures->lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(scratch);
+
+static ssize_t backplane_id_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+
+	return sprintf(buf, "%s\n", nirtfeatures->bpstring);
+}
+static DEVICE_ATTR_RO(backplane_id);
+
+static const char *const nirtfeatures_reset_source_strings[] = {
+	"button", "processor", "fpga", "watchdog", "software",
+};
+
+static ssize_t reset_source_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	u8 data;
+	int i;
+
+	data = inb(nirtfeatures->io_base + NIRTF_PROC_RESET_SOURCE);
+
+	for (i = 0; i < ARRAY_SIZE(nirtfeatures_reset_source_strings); i++)
+		if ((1 << i) & data)
+			return sprintf(buf, "%s\n",
+				       nirtfeatures_reset_source_strings[i]);
+
+	return sprintf(buf, "poweron\n");
+}
+static DEVICE_ATTR_RO(reset_source);
+
+static ssize_t mode_show(struct device *dev, char *buf, unsigned int mask)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	u8 data;
+
+	data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE);
+	data &= mask;
+
+	return sprintf(buf, "%u\n", !!data);
+}
+
+static ssize_t no_fpga_sw_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	int ret;
+
+	spin_lock(&nirtfeatures->lock);
+
+	ret = mode_show(dev, buf, NIRTF_CONTROLLER_MODE_NO_FPGA_SW);
+
+	spin_unlock(&nirtfeatures->lock);
+
+	return ret;
+}
+
+static ssize_t no_fpga_sw_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct nirtfeatures *nirtfeatures = to_nirtfeatures(dev);
+	unsigned long tmp;
+	u8 data;
+
+	if (kstrtoul(buf, 0, &tmp) || (tmp > 1))
+		return -EINVAL;
+
+	spin_lock(&nirtfeatures->lock);
+
+	data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE);
+
+	if (tmp)
+		data |= NIRTF_CONTROLLER_MODE_NO_FPGA_SW;
+	else
+		data &= ~NIRTF_CONTROLLER_MODE_NO_FPGA_SW;
+
+	outb(data, nirtfeatures->io_base + NIRTF_CONTROLLER_MODE);
+
+	spin_unlock(&nirtfeatures->lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(no_fpga_sw);
+
+static ssize_t soft_reset_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_HARD_BOOT_N);
+}
+static DEVICE_ATTR_RO(soft_reset);
+
+static ssize_t no_fpga_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_NO_FPGA);
+}
+static DEVICE_ATTR_RO(no_fpga);
+
+static ssize_t recovery_mode_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_RECOVERY);
+}
+static DEVICE_ATTR_RO(recovery_mode);
+
+static ssize_t console_out_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_CONSOLE_OUT);
+}
+static DEVICE_ATTR_RO(console_out);
+
+static ssize_t ip_reset_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_IP_RESET);
+}
+static DEVICE_ATTR_RO(ip_reset);
+
+static ssize_t safe_mode_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	return mode_show(dev, buf, NIRTF_CONTROLLER_MODE_SAFE);
+}
+static DEVICE_ATTR_RO(safe_mode);
+
+static const struct attribute *nirtfeatures_attrs[] = {
+	&dev_attr_revision.attr,
+	&dev_attr_scratch.attr,
+	&dev_attr_backplane_id.attr,
+	&dev_attr_reset_source.attr,
+	&dev_attr_no_fpga_sw.attr,
+	&dev_attr_soft_reset.attr,
+	&dev_attr_no_fpga.attr,
+	&dev_attr_recovery_mode.attr,
+	&dev_attr_console_out.attr,
+	&dev_attr_ip_reset.attr,
+	&dev_attr_safe_mode.attr,
+	NULL
+};
+
+/* LEDs */
+
+static void nirtfeatures_led_brightness_set(struct led_classdev *led_cdev,
+					    enum led_brightness brightness)
+{
+	struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev;
+	u8 data;
+
+	spin_lock(&led->nirtfeatures->lock);
+
+	data = inb(led->nirtfeatures->io_base + led->address);
+	data &= ~led->mask;
+	if (!!brightness)
+		data |= led->mask;
+	outb(data, led->nirtfeatures->io_base + led->address);
+
+	if (led->pattern_hi_addr && led->pattern_lo_addr) {
+		/* Write the high byte first. */
+		outb(brightness >> 8,
+		     led->nirtfeatures->io_base + led->pattern_hi_addr);
+		outb(brightness & 0xFF,
+		     led->nirtfeatures->io_base + led->pattern_lo_addr);
+	}
+
+	spin_unlock(&led->nirtfeatures->lock);
+}
+
+static enum led_brightness
+nirtfeatures_led_brightness_get(struct led_classdev *led_cdev)
+{
+	struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev;
+	u8 data;
+
+	data = inb(led->nirtfeatures->io_base + led->address);
+
+	/*
+	 * For the yellow status LED, the blink pattern used for brightness
+	 * on write is write-only, so we just return on/off for all LEDs.
+	 */
+	return (data & led->mask) ? led_cdev->max_brightness : 0;
+}
+
+static struct nirtfeatures_led nirtfeatures_leds_common[] = {
+	{
+		{
+			.name = "nilrt:user1:green",
+		},
+		.address = NIRTF_RT_LEDS,
+		.mask = NIRTF_RT_LEDS_USER1_GREEN,
+	},
+	{
+		{
+			.name = "nilrt:user1:yellow",
+		},
+		.address = NIRTF_RT_LEDS,
+		.mask = NIRTF_RT_LEDS_USER1_YELLOW,
+	},
+	{
+		{
+			.name = "nilrt:status:red",
+		},
+		.address = NIRTF_SYSTEM_LEDS,
+		.mask = NIRTF_SYSTEM_LEDS_STATUS_RED,
+	},
+	{
+		{
+			.name = "nilrt:status:yellow",
+			.max_brightness = 0xFFFF,
+		},
+		.address = NIRTF_SYSTEM_LEDS,
+		.mask = NIRTF_SYSTEM_LEDS_STATUS_YELLOW,
+		.pattern_hi_addr = NIRTF_STATUS_LED_SHIFT1,
+		.pattern_lo_addr = NIRTF_STATUS_LED_SHIFT0,
+	},
+	{
+		{
+			.name = "nilrt:power:green",
+		},
+		.address = NIRTF_SYSTEM_LEDS,
+		.mask = NIRTF_SYSTEM_LEDS_POWER_GREEN,
+	},
+	{
+		{
+			.name = "nilrt:power:yellow",
+		},
+		.address = NIRTF_SYSTEM_LEDS,
+		.mask = NIRTF_SYSTEM_LEDS_POWER_YELLOW,
+	},
+};
+
+static struct nirtfeatures_led nirtfeatures_leds_cdaq[] = {
+	{
+		{
+			.name = "nilrt:user2:green",
+		},
+		.address = NIRTF_RT_LEDS,
+		.mask = NIRTF_RT_LEDS_USER2_GREEN,
+	},
+	{
+		{
+			.name = "nilrt:user2:yellow",
+		},
+		.address = NIRTF_RT_LEDS,
+		.mask = NIRTF_RT_LEDS_USER2_YELLOW,
+	},
+};
+
+static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures)
+{
+	int i;
+	int err;
+
+	struct nirtfeatures_led *leds = nirtfeatures_leds_common;
+
+	for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); i++) {
+
+		leds[i].nirtfeatures = nirtfeatures;
+
+		if (leds[i].cdev.max_brightness == 0)
+			leds[i].cdev.max_brightness = 1;
+
+		leds[i].cdev.brightness_set = nirtfeatures_led_brightness_set;
+		leds[i].cdev.brightness_get = nirtfeatures_led_brightness_get;
+
+		err =
+		    devm_led_classdev_register(&nirtfeatures->acpi_device->dev,
+						 &leds[i].cdev);
+		if (err)
+			return err;
+	}
+
+	for (i = 0; i < nirtfeatures->num_extra_leds; ++i) {
+
+		nirtfeatures->extra_leds[i].nirtfeatures = nirtfeatures;
+
+		if (nirtfeatures->extra_leds[i].cdev.max_brightness == 0)
+			nirtfeatures->extra_leds[i].cdev.max_brightness = 1;
+
+		nirtfeatures->extra_leds[i].cdev.brightness_set =
+			nirtfeatures_led_brightness_set;
+
+		nirtfeatures->extra_leds[i].cdev.brightness_get =
+			nirtfeatures_led_brightness_get;
+
+		err = devm_led_classdev_register(
+				&nirtfeatures->acpi_device->dev,
+				&nirtfeatures->extra_leds[i].cdev);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/* ACPI driver */
+
+static acpi_status nirtfeatures_resources(struct acpi_resource *res,
+					  void *data)
+{
+	struct nirtfeatures *nirtfeatures = data;
+	u8 io_size;
+
+	if (res->type == ACPI_RESOURCE_TYPE_IO) {
+		if (nirtfeatures->io_base != 0) {
+			dev_err(&nirtfeatures->acpi_device->dev,
+				"too many IO resources\n");
+			return AE_ALREADY_EXISTS;
+		}
+
+		nirtfeatures->io_base = res->data.io.minimum;
+		io_size = res->data.io.address_length;
+
+		if (io_size != NIRTF_IO_SIZE) {
+			dev_err(&nirtfeatures->acpi_device->dev,
+				"invalid IO size 0x%02x\n", io_size);
+			return AE_ERROR;
+		}
+
+		if (!devm_request_region(&nirtfeatures->acpi_device->dev,
+					 nirtfeatures->io_base, io_size,
+					 MODULE_NAME)) {
+			dev_err(&nirtfeatures->acpi_device->dev,
+				"failed to get memory region\n");
+			return AE_NO_MEMORY;
+		}
+	}
+
+	return AE_OK;
+}
+
+static int nirtfeatures_acpi_add(struct acpi_device *device)
+{
+	struct nirtfeatures *nirtfeatures;
+	acpi_status acpi_ret;
+	u8 bpinfo;
+	int err;
+
+	nirtfeatures = devm_kzalloc(&device->dev, sizeof(*nirtfeatures),
+				    GFP_KERNEL);
+	if (!nirtfeatures)
+		return -ENOMEM;
+
+	device->driver_data = nirtfeatures;
+	nirtfeatures->acpi_device = device;
+
+	acpi_ret = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+				       nirtfeatures_resources, nirtfeatures);
+	if (ACPI_FAILURE(acpi_ret) || nirtfeatures->io_base == 0) {
+		dev_err(&device->dev, "failed to get resources\n");
+		return -ENODEV;
+	}
+
+	bpinfo = inb(nirtfeatures->io_base + NIRTF_PLATFORM_MISC);
+	bpinfo &= NIRTF_PLATFORM_MISC_ID_MASK;
+
+	switch (bpinfo) {
+	case NIRTF_PLATFORM_MISC_ID_MANHATTAN:
+		nirtfeatures->bpstring = "Manhattan";
+		break;
+	case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD:
+		nirtfeatures->bpstring = "Hammerhead";
+		nirtfeatures->extra_leds = nirtfeatures_leds_cdaq;
+		nirtfeatures->num_extra_leds =
+			ARRAY_SIZE(nirtfeatures_leds_cdaq);
+		break;
+	case NIRTF_PLATFORM_MISC_ID_WINGHEAD:
+		nirtfeatures->bpstring = "Winghead";
+		nirtfeatures->extra_leds = nirtfeatures_leds_cdaq;
+		nirtfeatures->num_extra_leds =
+			ARRAY_SIZE(nirtfeatures_leds_cdaq);
+		break;
+	default:
+		dev_err(&nirtfeatures->acpi_device->dev,
+			"Unrecognized backplane type %u\n", bpinfo);
+		nirtfeatures->bpstring = "Unknown";
+		break;
+	}
+
+	spin_lock_init(&nirtfeatures->lock);
+
+	nirtfeatures->revision[0] = inb(nirtfeatures->io_base + NIRTF_YEAR);
+	nirtfeatures->revision[1] = inb(nirtfeatures->io_base + NIRTF_MONTH);
+	nirtfeatures->revision[2] = inb(nirtfeatures->io_base + NIRTF_DAY);
+	nirtfeatures->revision[3] = inb(nirtfeatures->io_base + NIRTF_HOUR);
+	nirtfeatures->revision[4] = inb(nirtfeatures->io_base + NIRTF_MINUTE);
+
+	err = nirtfeatures_create_leds(nirtfeatures);
+	if (err) {
+		dev_err(&device->dev, "could not create LEDs\n");
+		return err;
+	}
+
+	err = sysfs_create_files(&device->dev.kobj, nirtfeatures_attrs);
+	if (err) {
+		dev_err(&device->dev, "could not create sysfs attributes\n");
+		return err;
+	}
+
+	dev_dbg(&nirtfeatures->acpi_device->dev,
+		"%s backplane, revision 20%02X/%02X/%02X %02X:%02X, io_base 0x%04X\n",
+		nirtfeatures->bpstring, nirtfeatures->revision[0],
+		nirtfeatures->revision[1], nirtfeatures->revision[2],
+		nirtfeatures->revision[3], nirtfeatures->revision[4],
+		nirtfeatures->io_base);
+
+	return 0;
+}
+
+static int nirtfeatures_acpi_remove(struct acpi_device *device)
+{
+	sysfs_remove_files(&device->dev.kobj, nirtfeatures_attrs);
+
+	return 0;
+}
+
+static const struct acpi_device_id nirtfeatures_device_ids[] = {
+	{"NIC775D", 0},
+	{"", 0},
+};
+
+static struct acpi_driver nirtfeatures_acpi_driver = {
+	.name = MODULE_NAME,
+	.ids = nirtfeatures_device_ids,
+	.ops = {
+		.add = nirtfeatures_acpi_add,
+		.remove = nirtfeatures_acpi_remove,
+	},
+};
+
+module_acpi_driver(nirtfeatures_acpi_driver);
+
+MODULE_DEVICE_TABLE(acpi, nirtfeatures_device_ids);
+MODULE_DESCRIPTION("NI RT Features");
+MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@...com>");
+MODULE_LICENSE("GPL");
-- 
2.7.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ