summaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig22
-rw-r--r--drivers/misc/msi-laptop.c2
-rw-r--r--drivers/misc/sony-laptop.c204
-rw-r--r--drivers/misc/thinkpad_acpi.c321
-rw-r--r--drivers/misc/thinkpad_acpi.h38
5 files changed, 415 insertions, 172 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index df89367032c3..e0a1ff927a5b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -149,6 +149,7 @@ config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on X86 && ACPI
select BACKLIGHT_CLASS_DEVICE
+ depends on INPUT
---help---
This mini-driver drives the SNC and SPIC devices present in the ACPI
BIOS of the Sony Vaio laptops.
@@ -171,6 +172,7 @@ config THINKPAD_ACPI
select BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
+ depends on INPUT
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
@@ -217,25 +219,5 @@ config THINKPAD_ACPI_BAY
If you are not sure, say Y here.
-config THINKPAD_ACPI_INPUT_ENABLED
- bool "Enable input layer support by default"
- depends on THINKPAD_ACPI
- default n
- ---help---
- This option enables thinkpad-acpi hot key handling over the input
- layer at driver load time. When it is unset, the driver does not
- enable hot key handling by default, and also starts up with a mostly
- empty keymap.
-
- This option should be enabled if you have a new enough HAL or other
- userspace support that properly handles the thinkpad-acpi event
- device. It auto-tunes the hot key support to those reported by the
- firmware and enables it automatically.
-
- If unsure, say N here to retain the old behaviour of ibm-acpi, and
- thinkpad-acpi up to kernel 2.6.21: userspace will have to enable and
- set up the thinkpad-acpi hot key handling using the sysfs interace
- after loading the driver.
-
endif # MISC_DEVICES
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c
index 932a415197b3..349be934db7c 100644
--- a/drivers/misc/msi-laptop.c
+++ b/drivers/misc/msi-laptop.c
@@ -353,7 +353,7 @@ static int __init msi_init(void)
if (IS_ERR(msibl_device))
return PTR_ERR(msibl_device);
- msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1,
+ msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
ret = platform_driver_register(&msipf_driver);
if (ret)
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
index d38ddce592c0..f248080828f2 100644
--- a/drivers/misc/sony-laptop.c
+++ b/drivers/misc/sony-laptop.c
@@ -1173,7 +1173,8 @@ static struct acpi_driver sony_nc_driver = {
#define SONYPI_TYPE3_OFFSET 0x12
struct sony_pic_ioport {
- struct acpi_resource_io io;
+ struct acpi_resource_io io1;
+ struct acpi_resource_io io2;
struct list_head list;
};
@@ -1443,11 +1444,11 @@ static u8 sony_pic_call1(u8 dev)
{
u8 v1, v2;
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
ITERATIONS_LONG);
- outb(dev, spic_dev.cur_ioport->io.minimum + 4);
- v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4);
- v2 = inb_p(spic_dev.cur_ioport->io.minimum);
+ outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+ v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);
+ v2 = inb_p(spic_dev.cur_ioport->io1.minimum);
dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1);
return v2;
}
@@ -1456,13 +1457,13 @@ static u8 sony_pic_call2(u8 dev, u8 fn)
{
u8 v1;
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
ITERATIONS_LONG);
- outb(dev, spic_dev.cur_ioport->io.minimum + 4);
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2,
+ outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
ITERATIONS_LONG);
- outb(fn, spic_dev.cur_ioport->io.minimum);
- v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+ outb(fn, spic_dev.cur_ioport->io1.minimum);
+ v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
dprintk("sony_pic_call2: 0x%.4x\n", v1);
return v1;
}
@@ -1471,13 +1472,13 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
{
u8 v1;
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
- outb(dev, spic_dev.cur_ioport->io.minimum + 4);
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
- outb(fn, spic_dev.cur_ioport->io.minimum);
- wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
- outb(v, spic_dev.cur_ioport->io.minimum);
- v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(fn, spic_dev.cur_ioport->io1.minimum);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(v, spic_dev.cur_ioport->io1.minimum);
+ v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
dprintk("sony_pic_call3: 0x%.4x\n", v1);
return v1;
}
@@ -2074,7 +2075,18 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ {
+ /* start IO enumeration */
+ struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
+ if (!ioport)
+ return AE_ERROR;
+
+ list_add(&ioport->list, &dev->ioports);
+ return AE_OK;
+ }
+
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+ /* end IO enumeration */
return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ:
@@ -2101,7 +2113,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
if (!interrupt)
return AE_ERROR;
- list_add_tail(&interrupt->list, &dev->interrupts);
+ list_add(&interrupt->list, &dev->interrupts);
interrupt->irq.triggering = p->triggering;
interrupt->irq.polarity = p->polarity;
interrupt->irq.sharable = p->sharable;
@@ -2113,18 +2125,27 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
case ACPI_RESOURCE_TYPE_IO:
{
struct acpi_resource_io *io = &resource->data.io;
- struct sony_pic_ioport *ioport = NULL;
+ struct sony_pic_ioport *ioport =
+ list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
if (!io) {
dprintk("Blank IO resource\n");
return AE_OK;
}
- ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
- if (!ioport)
+ if (!ioport->io1.minimum) {
+ memcpy(&ioport->io1, io, sizeof(*io));
+ dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,
+ ioport->io1.address_length);
+ }
+ else if (!ioport->io2.minimum) {
+ memcpy(&ioport->io2, io, sizeof(*io));
+ dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,
+ ioport->io2.address_length);
+ }
+ else {
+ printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
return AE_ERROR;
-
- list_add_tail(&ioport->list, &dev->ioports);
- memcpy(&ioport->io, io, sizeof(*io));
+ }
return AE_OK;
}
default:
@@ -2199,10 +2220,22 @@ static int sony_pic_enable(struct acpi_device *device,
{
acpi_status status;
int result = 0;
+ /* Type 1 resource layout is:
+ * IO
+ * IO
+ * IRQNoFlags
+ * End
+ *
+ * Type 2 and 3 resource layout is:
+ * IO
+ * IRQNoFlags
+ * End
+ */
struct {
- struct acpi_resource io_res;
- struct acpi_resource irq_res;
- struct acpi_resource end;
+ struct acpi_resource res1;
+ struct acpi_resource res2;
+ struct acpi_resource res3;
+ struct acpi_resource res4;
} *resource;
struct acpi_buffer buffer = { 0, NULL };
@@ -2217,21 +2250,49 @@ static int sony_pic_enable(struct acpi_device *device,
buffer.length = sizeof(*resource) + 1;
buffer.pointer = resource;
- /* setup io resource */
- resource->io_res.type = ACPI_RESOURCE_TYPE_IO;
- resource->io_res.length = sizeof(struct acpi_resource);
- memcpy(&resource->io_res.data.io, &ioport->io,
- sizeof(struct acpi_resource_io));
+ /* setup Type 1 resources */
+ if (spic_dev.model == SONYPI_DEVICE_TYPE1) {
- /* setup irq resource */
- resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ;
- resource->irq_res.length = sizeof(struct acpi_resource);
- memcpy(&resource->irq_res.data.irq, &irq->irq,
- sizeof(struct acpi_resource_irq));
- /* we requested a shared irq */
- resource->irq_res.data.irq.sharable = ACPI_SHARED;
+ /* setup io resources */
+ resource->res1.type = ACPI_RESOURCE_TYPE_IO;
+ resource->res1.length = sizeof(struct acpi_resource);
+ memcpy(&resource->res1.data.io, &ioport->io1,
+ sizeof(struct acpi_resource_io));
- resource->end.type = ACPI_RESOURCE_TYPE_END_TAG;
+ resource->res2.type = ACPI_RESOURCE_TYPE_IO;
+ resource->res2.length = sizeof(struct acpi_resource);
+ memcpy(&resource->res2.data.io, &ioport->io2,
+ sizeof(struct acpi_resource_io));
+
+ /* setup irq resource */
+ resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;
+ resource->res3.length = sizeof(struct acpi_resource);
+ memcpy(&resource->res3.data.irq, &irq->irq,
+ sizeof(struct acpi_resource_irq));
+ /* we requested a shared irq */
+ resource->res3.data.irq.sharable = ACPI_SHARED;
+
+ resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
+
+ }
+ /* setup Type 2/3 resources */
+ else {
+ /* setup io resource */
+ resource->res1.type = ACPI_RESOURCE_TYPE_IO;
+ resource->res1.length = sizeof(struct acpi_resource);
+ memcpy(&resource->res1.data.io, &ioport->io1,
+ sizeof(struct acpi_resource_io));
+
+ /* setup irq resource */
+ resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;
+ resource->res2.length = sizeof(struct acpi_resource);
+ memcpy(&resource->res2.data.irq, &irq->irq,
+ sizeof(struct acpi_resource_irq));
+ /* we requested a shared irq */
+ resource->res2.data.irq.sharable = ACPI_SHARED;
+
+ resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
+ }
/* Attempt to set the resource */
dprintk("Evaluating _SRS\n");
@@ -2239,7 +2300,7 @@ static int sony_pic_enable(struct acpi_device *device,
/* check for total failure */
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR DRV_PFX "Error evaluating _SRS");
+ printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
result = -ENODEV;
goto end;
}
@@ -2268,11 +2329,14 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
- ev = inb_p(dev->cur_ioport->io.minimum);
- data_mask = inb_p(dev->cur_ioport->io.minimum + dev->evport_offset);
+ ev = inb_p(dev->cur_ioport->io1.minimum);
+ if (dev->cur_ioport->io2.minimum)
+ data_mask = inb_p(dev->cur_ioport->io2.minimum);
+ else
+ data_mask = inb_p(dev->cur_ioport->io1.minimum + dev->evport_offset);
dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
- ev, data_mask, dev->cur_ioport->io.minimum, dev->evport_offset);
+ ev, data_mask, dev->cur_ioport->io1.minimum, dev->evport_offset);
if (ev == 0x00 || ev == 0xff)
return IRQ_HANDLED;
@@ -2323,8 +2387,11 @@ static int sony_pic_remove(struct acpi_device *device, int type)
}
free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
- release_region(spic_dev.cur_ioport->io.minimum,
- spic_dev.cur_ioport->io.address_length);
+ release_region(spic_dev.cur_ioport->io1.minimum,
+ spic_dev.cur_ioport->io1.address_length);
+ if (spic_dev.cur_ioport->io2.minimum)
+ release_region(spic_dev.cur_ioport->io2.minimum,
+ spic_dev.cur_ioport->io2.address_length);
sonypi_compat_exit();
@@ -2397,14 +2464,36 @@ static int sony_pic_add(struct acpi_device *device)
goto err_remove_input;
/* request io port */
- list_for_each_entry(io, &spic_dev.ioports, list) {
- if (request_region(io->io.minimum, io->io.address_length,
+ list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
+ if (request_region(io->io1.minimum, io->io1.address_length,
"Sony Programable I/O Device")) {
- dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n",
- io->io.minimum, io->io.maximum,
- io->io.address_length);
- spic_dev.cur_ioport = io;
- break;
+ dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
+ io->io1.minimum, io->io1.maximum,
+ io->io1.address_length);
+ /* Type 1 have 2 ioports */
+ if (io->io2.minimum) {
+ if (request_region(io->io2.minimum,
+ io->io2.address_length,
+ "Sony Programable I/O Device")) {
+ dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
+ io->io2.minimum, io->io2.maximum,
+ io->io2.address_length);
+ spic_dev.cur_ioport = io;
+ break;
+ }
+ else {
+ dprintk("Unable to get I/O port2: "
+ "0x%.4x (0x%.4x) + 0x%.2x\n",
+ io->io2.minimum, io->io2.maximum,
+ io->io2.address_length);
+ release_region(io->io1.minimum,
+ io->io1.address_length);
+ }
+ }
+ else {
+ spic_dev.cur_ioport = io;
+ break;
+ }
}
}
if (!spic_dev.cur_ioport) {
@@ -2414,7 +2503,7 @@ static int sony_pic_add(struct acpi_device *device)
}
/* request IRQ */
- list_for_each_entry(irq, &spic_dev.interrupts, list) {
+ list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
IRQF_SHARED, "sony-laptop", &spic_dev)) {
dprintk("IRQ: %d - triggering: %d - "
@@ -2462,8 +2551,11 @@ err_free_irq:
free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
err_release_region:
- release_region(spic_dev.cur_ioport->io.minimum,
- spic_dev.cur_ioport->io.address_length);
+ release_region(spic_dev.cur_ioport->io1.minimum,
+ spic_dev.cur_ioport->io1.address_length);
+ if (spic_dev.cur_ioport->io2.minimum)
+ release_region(spic_dev.cur_ioport->io2.minimum,
+ spic_dev.cur_ioport->io2.address_length);
err_remove_compat:
sonypi_compat_exit();
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index bb8956d0c104..37891a8c030a 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -21,8 +21,8 @@
* 02110-1301, USA.
*/
-#define IBM_VERSION "0.15"
-#define TPACPI_SYSFS_VERSION 0x010000
+#define IBM_VERSION "0.16"
+#define TPACPI_SYSFS_VERSION 0x020000
/*
* Changelog:
@@ -117,6 +117,12 @@ IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
#define __unused __attribute__ ((unused))
+static enum {
+ TPACPI_LIFE_INIT = 0,
+ TPACPI_LIFE_RUNNING,
+ TPACPI_LIFE_EXITING,
+} tpacpi_lifecycle;
+
/****************************************************************************
****************************************************************************
*
@@ -342,6 +348,9 @@ static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct ibm_struct *ibm = data;
+ if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
+ return;
+
if (!ibm || !ibm->acpi || !ibm->acpi->notify)
return;
@@ -517,8 +526,10 @@ static char *next_cmd(char **cmds)
****************************************************************************/
static struct platform_device *tpacpi_pdev;
+static struct platform_device *tpacpi_sensors_pdev;
static struct class_device *tpacpi_hwmon;
static struct input_dev *tpacpi_inputdev;
+static struct mutex tpacpi_inputdev_send_mutex;
static int tpacpi_resume_handler(struct platform_device *pdev)
@@ -543,6 +554,12 @@ static struct platform_driver tpacpi_pdriver = {
.resume = tpacpi_resume_handler,
};
+static struct platform_driver tpacpi_hwmon_pdriver = {
+ .driver = {
+ .name = IBM_HWMON_DRVR_NAME,
+ .owner = THIS_MODULE,
+ },
+};
/*************************************************************************
* thinkpad-acpi driver attributes
@@ -692,6 +709,8 @@ static int parse_strtoul(const char *buf,
{
char *endp;
+ while (*buf && isspace(*buf))
+ buf++;
*value = simple_strtoul(buf, &endp, 0);
while (*endp && isspace(*endp))
endp++;
@@ -906,9 +925,26 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
static struct device_attribute dev_attr_hotkey_radio_sw =
__ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
+/* sysfs hotkey report_mode -------------------------------------------- */
+static ssize_t hotkey_report_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
+}
+
+static struct device_attribute dev_attr_hotkey_report_mode =
+ __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
+
/* --------------------------------------------------------------------- */
-static struct attribute *hotkey_mask_attributes[] = {
+static struct attribute *hotkey_attributes[] __initdata = {
+ &dev_attr_hotkey_enable.attr,
+ &dev_attr_hotkey_report_mode.attr,
+};
+
+static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_mask.attr,
&dev_attr_hotkey_bios_enabled.attr,
&dev_attr_hotkey_bios_mask.attr,
@@ -972,6 +1008,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
int res, i;
int status;
+ int hkeyv;
vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
@@ -987,27 +1024,45 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
str_supported(tp_features.hotkey));
if (tp_features.hotkey) {
- hotkey_dev_attributes = create_attr_set(7, NULL);
+ hotkey_dev_attributes = create_attr_set(8, NULL);
if (!hotkey_dev_attributes)
return -ENOMEM;
- res = add_to_attr_set(hotkey_dev_attributes,
- &dev_attr_hotkey_enable.attr);
+ res = add_many_to_attr_set(hotkey_dev_attributes,
+ hotkey_attributes,
+ ARRAY_SIZE(hotkey_attributes));
if (res)
return res;
/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
- A30, R30, R31, T20-22, X20-21, X22-24 */
- tp_features.hotkey_mask =
- acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
+ A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
+ for HKEY interface version 0x100 */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) != 1) {
+ printk(IBM_ERR "unknown version of the "
+ "HKEY interface: 0x%x\n", hkeyv);
+ printk(IBM_ERR "please report this to %s\n",
+ IBM_MAIL);
+ } else {
+ /*
+ * MHKV 0x100 in A31, R40, R40e,
+ * T4x, X31, and later
+ * */
+ tp_features.hotkey_mask = 1;
+ }
+ }
vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
str_supported(tp_features.hotkey_mask));
if (tp_features.hotkey_mask) {
- /* MHKA available in A31, R40, R40e, T4x, X31, and later */
if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
- "MHKA", "qd"))
+ "MHKA", "qd")) {
+ printk(IBM_ERR
+ "missing MHKA handler, "
+ "please report this to %s\n",
+ IBM_MAIL);
hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */
+ }
}
res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
@@ -1055,11 +1110,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
TPACPI_HOTKEY_MAP_SIZE);
}
-#ifndef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
- for (i = 0; i < 12; i++)
- hotkey_keycode_map[i] = KEY_UNKNOWN;
-#endif /* ! CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
-
set_bit(EV_KEY, tpacpi_inputdev->evbit);
set_bit(EV_MSC, tpacpi_inputdev->evbit);
set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
@@ -1081,14 +1131,17 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
set_bit(SW_RADIO, tpacpi_inputdev->swbit);
}
-#ifdef CONFIG_THINKPAD_ACPI_INPUT_ENABLED
dbg_printk(TPACPI_DBG_INIT,
"enabling hot key handling\n");
res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask)
| hotkey_orig_mask);
if (res)
return res;
-#endif /* CONFIG_THINKPAD_ACPI_INPUT_ENABLED */
+
+ dbg_printk(TPACPI_DBG_INIT,
+ "legacy hot key reporting over procfs %s\n",
+ (hotkey_report_mode < 2) ?
+ "enabled" : "disabled");
}
return (tp_features.hotkey)? 0 : 1;
@@ -1115,6 +1168,8 @@ static void tpacpi_input_send_key(unsigned int scancode,
unsigned int keycode)
{
if (keycode != KEY_RESERVED) {
+ mutex_lock(&tpacpi_inputdev_send_mutex);
+
input_report_key(tpacpi_inputdev, keycode, 1);
if (keycode == KEY_UNKNOWN)
input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
@@ -1126,6 +1181,8 @@ static void tpacpi_input_send_key(unsigned int scancode,
input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
scancode);
input_sync(tpacpi_inputdev);
+
+ mutex_unlock(&tpacpi_inputdev_send_mutex);
}
}
@@ -1133,67 +1190,102 @@ static void tpacpi_input_send_radiosw(void)
{
int wlsw;
- if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw))
+ mutex_lock(&tpacpi_inputdev_send_mutex);
+
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
input_report_switch(tpacpi_inputdev,
SW_RADIO, !!wlsw);
+ input_sync(tpacpi_inputdev);
+ }
+
+ mutex_unlock(&tpacpi_inputdev_send_mutex);
}
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
{
u32 hkey;
unsigned int keycode, scancode;
- int sendacpi = 1;
-
- if (event == 0x80 && acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
- if (tpacpi_inputdev->users > 0) {
- switch (hkey >> 12) {
- case 1:
- /* 0x1000-0x1FFF: key presses */
- scancode = hkey & 0xfff;
- if (scancode > 0 && scancode < 0x21) {
- scancode--;
- keycode = hotkey_keycode_map[scancode];
- tpacpi_input_send_key(scancode, keycode);
- sendacpi = (keycode == KEY_RESERVED
- || keycode == KEY_UNKNOWN);
- } else {
- printk(IBM_ERR
- "hotkey 0x%04x out of range for keyboard map\n",
- hkey);
- }
- break;
- case 5:
- /* 0x5000-0x5FFF: LID */
- /* we don't handle it through this path, just
- * eat up known LID events */
- if (hkey != 0x5001 && hkey != 0x5002) {
- printk(IBM_ERR
- "unknown LID-related hotkey event: 0x%04x\n",
- hkey);
- }
+ int send_acpi_ev;
+ int ignore_acpi_ev;
+
+ if (event != 0x80) {
+ printk(IBM_ERR "unknown HKEY notification event %d\n", event);
+ /* forward it to userspace, maybe it knows how to handle it */
+ acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+ ibm->acpi->device->dev.bus_id,
+ event, 0);
+ return;
+ }
+
+ while (1) {
+ if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
+ printk(IBM_ERR "failed to retrieve HKEY event\n");
+ return;
+ }
+
+ if (hkey == 0) {
+ /* queue empty */
+ return;
+ }
+
+ send_acpi_ev = 0;
+ ignore_acpi_ev = 0;
+
+ switch (hkey >> 12) {
+ case 1:
+ /* 0x1000-0x1FFF: key presses */
+ scancode = hkey & 0xfff;
+ if (scancode > 0 && scancode < 0x21) {
+ scancode--;
+ keycode = hotkey_keycode_map[scancode];
+ tpacpi_input_send_key(scancode, keycode);
+ } else {
+ printk(IBM_ERR
+ "hotkey 0x%04x out of range for keyboard map\n",
+ hkey);
+ send_acpi_ev = 1;
+ }
+ break;
+ case 5:
+ /* 0x5000-0x5FFF: LID */
+ /* we don't handle it through this path, just
+ * eat up known LID events */
+ if (hkey != 0x5001 && hkey != 0x5002) {
+ printk(IBM_ERR
+ "unknown LID-related HKEY event: 0x%04x\n",
+ hkey);
+ send_acpi_ev = 1;
+ } else {
+ ignore_acpi_ev = 1;
+ }
+ break;
+ case 7:
+ /* 0x7000-0x7FFF: misc */
+ if (tp_features.hotkey_wlsw && hkey == 0x7000) {
+ tpacpi_input_send_radiosw();
break;
- case 7:
- /* 0x7000-0x7FFF: misc */
- if (tp_features.hotkey_wlsw && hkey == 0x7000) {
- tpacpi_input_send_radiosw();
- sendacpi = 0;
- break;
- }
- /* fallthrough to default */
- default:
- /* case 2: dock-related */
- /* 0x2305 - T43 waking up due to bay lever eject while aslept */
- /* case 3: ultra-bay related. maybe bay in dock? */
- /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */
- printk(IBM_NOTICE "unhandled hotkey event 0x%04x\n", hkey);
}
+ /* fallthrough to default */
+ default:
+ /* case 2: dock-related */
+ /* 0x2305 - T43 waking up due to bay lever eject while aslept */
+ /* case 3: ultra-bay related. maybe bay in dock? */
+ /* 0x3003 - T43 after wake up by bay lever eject (0x2305) */
+ printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey);
+ send_acpi_ev = 1;
}
- if (sendacpi)
+ /* Legacy events */
+ if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) {
acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey);
- } else {
- printk(IBM_ERR "unknown hotkey notification event %d\n", event);
- acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
+ }
+
+ /* netlink events */
+ if (!ignore_acpi_ev && send_acpi_ev) {
+ acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+ ibm->acpi->device->dev.bus_id,
+ event, hkey);
+ }
}
}
@@ -2789,7 +2881,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
switch(thermal_read_mode) {
case TPACPI_THERMAL_TPEC_16:
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&thermal_temp_input16_group);
if (res)
return res;
@@ -2797,7 +2889,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
case TPACPI_THERMAL_TPEC_8:
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&thermal_temp_input8_group);
if (res)
return res;
@@ -2814,13 +2906,13 @@ static void thermal_exit(void)
{
switch(thermal_read_mode) {
case TPACPI_THERMAL_TPEC_16:
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
&thermal_temp_input16_group);
break;
case TPACPI_THERMAL_TPEC_8:
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
&thermal_temp_input16_group);
break;
case TPACPI_THERMAL_NONE:
@@ -3603,7 +3695,7 @@ static struct device_attribute dev_attr_fan_fan1_input =
__ATTR(fan1_input, S_IRUGO,
fan_fan1_input_show, NULL);
-/* sysfs fan fan_watchdog (driver) ------------------------------------- */
+/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
char *buf)
{
@@ -3745,10 +3837,10 @@ static int __init fan_init(struct ibm_init_struct *iibm)
if (fan_status_access_mode != TPACPI_FAN_NONE ||
fan_control_access_mode != TPACPI_FAN_WR_NONE) {
- rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&fan_attr_group);
if (!(rc < 0))
- rc = driver_create_file(&tpacpi_pdriver.driver,
+ rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
&driver_attr_fan_watchdog);
if (rc < 0)
return rc;
@@ -3831,8 +3923,8 @@ static void fan_exit(void)
vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");
/* FIXME: can we really do this unconditionally? */
- sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
- driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
+ sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
+ driver_remove_file(&tpacpi_hwmon_pdriver.driver, &driver_attr_fan_watchdog);
cancel_delayed_work(&fan_watchdog_task);
flush_scheduled_work();
@@ -3865,6 +3957,9 @@ static void fan_watchdog_fire(struct work_struct *ignored)
{
int rc;
+ if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
+ return;
+
printk(IBM_NOTICE "fan watchdog: enabling fan\n");
rc = fan_set_enable();
if (rc < 0) {
@@ -3885,7 +3980,8 @@ static void fan_watchdog_reset(void)
if (fan_watchdog_active)
cancel_delayed_work(&fan_watchdog_task);
- if (fan_watchdog_maxinterval > 0) {
+ if (fan_watchdog_maxinterval > 0 &&
+ tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
fan_watchdog_active = 1;
if (!schedule_delayed_work(&fan_watchdog_task,
msecs_to_jiffies(fan_watchdog_maxinterval
@@ -4279,6 +4375,19 @@ static struct ibm_struct fan_driver_data = {
****************************************************************************
****************************************************************************/
+/* sysfs name ---------------------------------------------------------- */
+static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", IBM_NAME);
+}
+
+static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
+ __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
/* /proc support */
static struct proc_dir_entry *proc_dir;
@@ -4623,6 +4732,9 @@ module_param_named(fan_control, fan_control_allowed, bool, 0);
static int brightness_mode;
module_param_named(brightness_mode, brightness_mode, int, 0);
+static unsigned int hotkey_report_mode;
+module_param(hotkey_report_mode, uint, 0);
+
#define IBM_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0)
@@ -4648,6 +4760,12 @@ static int __init thinkpad_acpi_module_init(void)
{
int ret, i;
+ tpacpi_lifecycle = TPACPI_LIFE_INIT;
+
+ /* Parameter checking */
+ if (hotkey_report_mode > 2)
+ return -EINVAL;
+
/* Driver-level probe */
get_thinkpad_model_data(&thinkpad_id);
@@ -4672,19 +4790,31 @@ static int __init thinkpad_acpi_module_init(void)
ret = platform_driver_register(&tpacpi_pdriver);
if (ret) {
- printk(IBM_ERR "unable to register platform driver\n");
+ printk(IBM_ERR "unable to register main platform driver\n");
thinkpad_acpi_module_exit();
return ret;
}
tp_features.platform_drv_registered = 1;
+ ret = platform_driver_register(&tpacpi_hwmon_pdriver);
+ if (ret) {
+ printk(IBM_ERR "unable to register hwmon platform driver\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ tp_features.sensors_pdrv_registered = 1;
+
ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
+ if (!ret) {
+ tp_features.platform_drv_attrs_registered = 1;
+ ret = tpacpi_create_driver_attributes(&tpacpi_hwmon_pdriver.driver);
+ }
if (ret) {
printk(IBM_ERR "unable to create sysfs driver attributes\n");
thinkpad_acpi_module_exit();
return ret;
}
- tp_features.platform_drv_attrs_registered = 1;
+ tp_features.sensors_pdrv_attrs_registered = 1;
/* Device initialization */
@@ -4697,7 +4827,26 @@ static int __init thinkpad_acpi_module_init(void)
thinkpad_acpi_module_exit();
return ret;
}
- tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
+ tpacpi_sensors_pdev = platform_device_register_simple(
+ IBM_HWMON_DRVR_NAME,
+ -1, NULL, 0);
+ if (IS_ERR(tpacpi_sensors_pdev)) {
+ ret = PTR_ERR(tpacpi_sensors_pdev);
+ tpacpi_sensors_pdev = NULL;
+ printk(IBM_ERR "unable to register hwmon platform device\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ ret = device_create_file(&tpacpi_sensors_pdev->dev,
+ &dev_attr_thinkpad_acpi_pdev_name);
+ if (ret) {
+ printk(IBM_ERR
+ "unable to create sysfs hwmon device attributes\n");
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
+ tp_features.sensors_pdev_attrs_registered = 1;
+ tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
if (IS_ERR(tpacpi_hwmon)) {
ret = PTR_ERR(tpacpi_hwmon);
tpacpi_hwmon = NULL;
@@ -4705,6 +4854,7 @@ static int __init thinkpad_acpi_module_init(void)
thinkpad_acpi_module_exit();
return ret;
}
+ mutex_init(&tpacpi_inputdev_send_mutex);
tpacpi_inputdev = input_allocate_device();
if (!tpacpi_inputdev) {
printk(IBM_ERR "unable to allocate input device\n");
@@ -4739,6 +4889,7 @@ static int __init thinkpad_acpi_module_init(void)
tp_features.input_device_registered = 1;
}
+ tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
return 0;
}
@@ -4746,6 +4897,8 @@ static void thinkpad_acpi_module_exit(void)
{
struct ibm_struct *ibm, *itmp;
+ tpacpi_lifecycle = TPACPI_LIFE_EXITING;
+
list_for_each_entry_safe_reverse(ibm, itmp,
&tpacpi_all_drivers,
all_drivers) {
@@ -4764,12 +4917,22 @@ static void thinkpad_acpi_module_exit(void)
if (tpacpi_hwmon)
hwmon_device_unregister(tpacpi_hwmon);
+ if (tp_features.sensors_pdev_attrs_registered)
+ device_remove_file(&tpacpi_sensors_pdev->dev,
+ &dev_attr_thinkpad_acpi_pdev_name);
+ if (tpacpi_sensors_pdev)
+ platform_device_unregister(tpacpi_sensors_pdev);
if (tpacpi_pdev)
platform_device_unregister(tpacpi_pdev);
+ if (tp_features.sensors_pdrv_attrs_registered)
+ tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
if (tp_features.platform_drv_attrs_registered)
tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
+ if (tp_features.sensors_pdrv_registered)
+ platform_driver_unregister(&tpacpi_hwmon_pdriver);
+
if (tp_features.platform_drv_registered)
platform_driver_unregister(&tpacpi_pdriver);
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h
index eee8809a50d9..c5fdd688cc99 100644
--- a/drivers/misc/thinkpad_acpi.h
+++ b/drivers/misc/thinkpad_acpi.h
@@ -58,13 +58,14 @@
#define IBM_NAME "thinkpad"
#define IBM_DESC "ThinkPad ACPI Extras"
-#define IBM_FILE "thinkpad_acpi"
+#define IBM_FILE IBM_NAME "_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"
#define IBM_PROC_DIR "ibm"
#define IBM_ACPI_EVENT_PREFIX "ibm"
#define IBM_DRVR_NAME IBM_FILE
+#define IBM_HWMON_DRVR_NAME IBM_NAME "_hwmon"
#define IBM_LOG IBM_FILE ": "
#define IBM_ERR KERN_ERR IBM_LOG
@@ -171,6 +172,7 @@ static int parse_strtoul(const char *buf, unsigned long max,
/* Device model */
static struct platform_device *tpacpi_pdev;
+static struct platform_device *tpacpi_sensors_pdev;
static struct class_device *tpacpi_hwmon;
static struct platform_driver tpacpi_pdriver;
static struct input_dev *tpacpi_inputdev;
@@ -181,6 +183,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv);
static int experimental;
static u32 dbg_level;
static int force_load;
+static unsigned int hotkey_report_mode;
static int thinkpad_acpi_module_init(void);
static void thinkpad_acpi_module_exit(void);
@@ -232,22 +235,25 @@ struct ibm_init_struct {
static struct {
#ifdef CONFIG_THINKPAD_ACPI_BAY
- u16 bay_status:1;
- u16 bay_eject:1;
- u16 bay_status2:1;
- u16 bay_eject2:1;
+ u32 bay_status:1;
+ u32 bay_eject:1;
+ u32 bay_status2:1;
+ u32 bay_eject2:1;
#endif
- u16 bluetooth:1;
- u16 hotkey:1;
- u16 hotkey_mask:1;
- u16 hotkey_wlsw:1;
- u16 light:1;
- u16 light_status:1;
- u16 wan:1;
- u16 fan_ctrl_status_undef:1;
- u16 input_device_registered:1;
- u16 platform_drv_registered:1;
- u16 platform_drv_attrs_registered:1;
+ u32 bluetooth:1;
+ u32 hotkey:1;
+ u32 hotkey_mask:1;
+ u32 hotkey_wlsw:1;
+ u32 light:1;
+ u32 light_status:1;
+ u32 wan:1;
+ u32 fan_ctrl_status_undef:1;
+ u32 input_device_registered:1;
+ u32 platform_drv_registered:1;
+ u32 platform_drv_attrs_registered:1;
+ u32 sensors_pdrv_registered:1;
+ u32 sensors_pdrv_attrs_registered:1;
+ u32 sensors_pdev_attrs_registered:1;
} tp_features;
struct thinkpad_id_data {