From 4a5e3638b11978262ab76bbb2062e57fefaaedba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 15 Jul 2008 09:42:57 -0600 Subject: ACPI: stop complaints about interrupt link End Tags and blank IRQ descriptors Silently ignore _PRS End Tags. We already ignore Start Dependent Functions in _PRS, and we already ignore End Tags in _CRS, so we might as well ignore End Tags in _PRS as well. Silently ignore _PRS IRQ descriptors that mention no interrupts. The spec allows this (section 6.4.2.1 in ACPI 3.0b spec), and it probably means the interrupt link can't be configured at all. This patch doesn't change any functional behavior; it just removes confusing complaints like these: ACPI: Blank IRQ resource ACPI: Resource is not an IRQ entry when parsing _PRS data "23 00 00 18 79 00" from an IBM xSeries 335 dual Pentium IV Xeon 2.40 GHz machine. For more details, see http://bugzilla.kernel.org/show_bug.cgi?id=11049 The "23 00 00 18" part is a three-byte-long small IRQ resource with no bits set in the IRQ mask ("00 00"), and level-triggered, active low, shareable ("18"). The "79 00" is an End Tag (type 0x7). It is superfluous since there is no Start Dependent Function tag and there are no resources after it, but it is harmless. Thanks to Gabriele Trombetti (aka Kurk) for reporting this and testing the patch. Signed-off-by: Bjorn Helgaas Signed-off-by: Andi Kleen --- drivers/acpi/pci_link.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 233c40c51684..89f3b2abfdc7 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -113,20 +113,23 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) switch (resource->type) { case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; if (!p || !p->interrupt_count) { - printk(KERN_WARNING PREFIX "Blank IRQ resource\n"); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank _PRS IRQ resource\n")); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", - p->interrupts[i]); + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; @@ -143,15 +146,16 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) &resource->data.extended_irq; if (!p || !p->interrupt_count) { printk(KERN_WARNING PREFIX - "Blank EXT IRQ resource\n"); + "Blank _PRS EXT IRQ resource\n"); return AE_OK; } for (i = 0; (i < p->interrupt_count && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", - p->interrupts[i]); + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); continue; } link->irq.possible[i] = p->interrupts[i]; @@ -163,7 +167,8 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) break; } default: - printk(KERN_ERR PREFIX "Resource is not an IRQ entry\n"); + printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n", + resource->type); return AE_OK; } @@ -199,6 +204,9 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: { struct acpi_resource_irq *p = &resource->data.irq; @@ -208,7 +216,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) * particularly those those w/ _STA disabled */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Blank IRQ resource\n")); + "Blank _CRS IRQ resource\n")); return AE_OK; } *irq = p->interrupts[0]; @@ -224,7 +232,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) * return at least 1 IRQ */ printk(KERN_WARNING PREFIX - "Blank EXT IRQ resource\n"); + "Blank _CRS EXT IRQ resource\n"); return AE_OK; } *irq = p->interrupts[0]; @@ -232,10 +240,11 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) } break; default: - printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type); - case ACPI_RESOURCE_TYPE_END_TAG: + printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n", + resource->type); return AE_OK; } + return AE_CTRL_TERMINATE; } -- cgit v1.2.3-58-ga151 From c2c789057f075022658b38b498755c29c1ba8055 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 17 Jul 2008 10:46:05 +0800 Subject: ACPI: Ignore _BQC object when registering backlight device According to acpi spec , the objectes of _BCL and _BCM are required if integrated LCD is present and supports brightness level and the _BQC is the optional object. So the _BQC object will be ignored when the backlight device is registered. At the same time when there is no _BQC object, the current brightness will be set to the maximum. http://bugzilla.kernel.org/show_bug.cgi?id=10206 Signed-off-by: Zhao Yakui Signed-off-by: Zhang Rui Signed-off-by: Andi Kleen --- drivers/acpi/video.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 64c889331f3b..e32b6c14d928 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -741,7 +741,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) max_level = acpi_video_init_brightness(device); - if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ + if (device->cap._BCL && device->cap._BCM && max_level > 0) { int result; static int count = 0; char *name; @@ -753,7 +753,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->backlight = backlight_device_register(name, NULL, device, &acpi_backlight_ops); device->backlight->props.max_brightness = device->brightness->count-3; - device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); + /* + * If there exists the _BQC object, the _BQC object will be + * called to get the current backlight brightness. Otherwise + * the brightness will be set to the maximum. + */ + if (device->cap._BQC) + device->backlight->props.brightness = + acpi_video_get_brightness(device->backlight); + else + device->backlight->props.brightness = + device->backlight->props.max_brightness; backlight_update_status(device->backlight); kfree(name); -- cgit v1.2.3-58-ga151 From ea51011a27db48ea0a80a5e20de3969b292d5d4d Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Mon, 14 Jul 2008 15:14:03 +0800 Subject: ACPI : Set FAN device to correct state in boot phase Subject:ACPI: Set FAN device to correct state in boot phase From: Zhao Yakui On some laptops when ACPI FAN driver is loaded, maybe the FAN device will be turned on. But if the temperature is below the threshold, the corresponding FAN device should be turned off in the course of loading thermal driver. So it is necessary to set the FAN device to the correct state in course of loading the thermal driver. http://bugzilla.kernel.org/show_bug.cgi?id=8049 Signed-off-by: Zhao Yakui Signed-off-by: Zhang Rui Signed-off-by: Andi Kleen --- drivers/acpi/thermal.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 84c795fb9b1e..9adfd180df6a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -769,6 +769,47 @@ static void acpi_thermal_run(unsigned long data) acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data); } +static void acpi_thermal_active_off(void *data) +{ + int result = 0; + struct acpi_thermal *tz = data; + int i = 0; + int j = 0; + struct acpi_thermal_active *active = NULL; + + if (!tz) { + printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); + return; + } + + result = acpi_thermal_get_temperature(tz); + if (result) + return; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + active = &(tz->trips.active[i]); + if (!active || !active->flags.valid) + break; + if (tz->temperature >= active->temperature) { + /* + * If the thermal temperature is greater than the + * active threshod, unnecessary to turn off the + * the active cooling device. + */ + continue; + } + /* + * Below Threshold? + * ---------------- + * Turn OFF all cooling devices associated with this + * threshold. + */ + for (j = 0; j < active->devices.count; j++) + result = acpi_bus_set_power(active->devices.handles[j], + ACPI_STATE_D3); + } +} + static void acpi_thermal_check(void *data) { int result = 0; @@ -1624,6 +1665,8 @@ static int acpi_thermal_add(struct acpi_device *device) init_timer(&tz->timer); + acpi_thermal_active_off(tz); + acpi_thermal_check(tz); status = acpi_install_notify_handler(device->handle, -- cgit v1.2.3-58-ga151 From b1d77fae0c429d1be84ca0c9e627d9ab0e2a6d0b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 18 Jul 2008 01:42:20 +0200 Subject: Revert "Fix FADT parsing" This reverts commit 01a5bba576b9364b33f61f0cd9fa70c2cf5535e2. There seem to be some FADTs around with bogus information in the v2 fields. Revert this patch for now until this can be properly resolved. Signed-off-by: Andi Kleen --- drivers/acpi/tables/tbfadt.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index ccb5b64bbef3..a4a41ba2484b 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -124,7 +124,7 @@ static struct acpi_fadt_info fadt_info_table[] = { static void inline acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, - u8 byte_width, u64 address) + u8 bit_width, u64 address) { /* @@ -136,7 +136,7 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, /* All other fields are byte-wide */ generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; - generic_address->bit_width = byte_width << 3; + generic_address->bit_width = bit_width; generic_address->bit_offset = 0; generic_address->access_width = 0; } @@ -343,11 +343,9 @@ static void acpi_tb_convert_fadt(void) * * The PM event blocks are split into two register blocks, first is the * PM Status Register block, followed immediately by the PM Enable Register - * block. Each is of length (xpm1x_event_block.bit_width/2) + * block. Each is of length (pm1_event_length/2) */ - WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1a_event_block.bit_width)); - pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT - .xpm1a_event_block.bit_width); + pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length); /* The PM1A register block is required */ @@ -362,17 +360,14 @@ static void acpi_tb_convert_fadt(void) /* The PM1B register block is optional, ignore if not present */ if (acpi_gbl_FADT.xpm1b_event_block.address) { - WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1b_event_block.bit_width)); - pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT - .xpm1b_event_block - .bit_width); acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable, pm1_register_length, (acpi_gbl_FADT.xpm1b_event_block. address + pm1_register_length)); /* Don't forget to copy space_id of the GAS */ acpi_gbl_xpm1b_enable.space_id = - acpi_gbl_FADT.xpm1b_event_block.space_id; + acpi_gbl_FADT.xpm1a_event_block.space_id; + } } -- cgit v1.2.3-58-ga151 From e1469c34eb623cd1945ef09bfd7de7bc2f9ff6b3 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 18 Jul 2008 01:43:08 +0200 Subject: Revert "dock: bay: Don't call acpi_walk_namespace() when ACPI is disabled." Revert double commit by mistake. Noticed by Thomas Gleixner. This reverts commit cc7e51666d82aedfd6b9a033ca1a10d71c21f1ca. Signed-off-by: Andi Kleen --- drivers/acpi/bay.c | 3 --- drivers/acpi/dock.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index e6caf5d42e0e..61b6c5beb2d3 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -377,9 +377,6 @@ static int __init bay_init(void) INIT_LIST_HEAD(&drive_bays); - if (acpi_disabled) - return -ENODEV; - if (acpi_disabled) return -ENODEV; diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 1e872e79db33..bb7c51f712bd 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -917,9 +917,6 @@ static int __init dock_init(void) dock_station = NULL; - if (acpi_disabled) - return 0; - if (acpi_disabled) return 0; -- cgit v1.2.3-58-ga151 From 725c3a2d70f958adee807c178178819a50f68a56 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 18 Jul 2008 09:12:49 +0200 Subject: Revert "ACPI: don't walk tables if ACPI was disabled" This reverts commit d1857056904d5f313f11184fcfa624652ff9620a. Double commit, noticed by Thomas Gleixner. Signed-off-by: Andi Kleen --- drivers/acpi/glue.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 0f2dd81736bd..2f173e83f8a7 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -335,9 +335,6 @@ static int __init acpi_rtc_init(void) { struct device *dev = get_rtc_dev(); - if (acpi_disabled) - return 0; - if (acpi_disabled) return 0; -- cgit v1.2.3-58-ga151 From 3a87208028ef59215a88a143c723ac0b83c11df0 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:49 -0300 Subject: ACPI: thinkpad-acpi: minor refactor on radio switch init Change the code of hotkey_init, wan_init and bluetooth_init a bit to make it much easier to add some Kconfig-selected debugging code later. Signed-off-by: Henrique de Moraes Holschuh --- drivers/misc/thinkpad_acpi.c | 49 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index b5969298f3d3..c800855be27f 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2167,9 +2167,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) printk(TPACPI_INFO "radio switch found; radios are %s\n", enabled(status, 0)); + } + if (tp_features.hotkey_wlsw) res = add_to_attr_set(hotkey_dev_attributes, &dev_attr_hotkey_radio_sw.attr); - } /* For X41t, X60t, X61t Tablets... */ if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { @@ -2646,18 +2647,19 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) str_supported(tp_features.bluetooth), status); + if (tp_features.bluetooth && + !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { + /* no bluetooth hardware present in system */ + tp_features.bluetooth = 0; + dbg_printk(TPACPI_DBG_INIT, + "bluetooth hardware not installed\n"); + } + if (tp_features.bluetooth) { - if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { - /* no bluetooth hardware present in system */ - tp_features.bluetooth = 0; - dbg_printk(TPACPI_DBG_INIT, - "bluetooth hardware not installed\n"); - } else { - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &bluetooth_attr_group); - if (res) - return res; - } + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); + if (res) + return res; } return (tp_features.bluetooth)? 0 : 1; @@ -2818,18 +2820,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) str_supported(tp_features.wan), status); + if (tp_features.wan && + !(status & TP_ACPI_WANCARD_HWPRESENT)) { + /* no wan hardware present in system */ + tp_features.wan = 0; + dbg_printk(TPACPI_DBG_INIT, + "wan hardware not installed\n"); + } + if (tp_features.wan) { - if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { - /* no wan hardware present in system */ - tp_features.wan = 0; - dbg_printk(TPACPI_DBG_INIT, - "wan hardware not installed\n"); - } else { - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &wan_attr_group); - if (res) - return res; - } + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) + return res; } return (tp_features.wan)? 0 : 1; -- cgit v1.2.3-58-ga151 From 733e27c1cc86afae2d9481838693661b3d839950 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:49 -0300 Subject: ACPI: thinkpad-acpi: consolidate wlsw notification function Rename tpacpi_input_send_radiosw() to tpacpi_send_radiosw_update(), and make it a central point to issue "radio switch changed state" notifications by consolidating also the poll() notification in the same function. Signed-off-by: Henrique de Moraes Holschuh --- drivers/misc/thinkpad_acpi.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index c800855be27f..9179f2367d42 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -1285,21 +1285,6 @@ static int hotkey_status_set(int status) return 0; } -static void tpacpi_input_send_radiosw(void) -{ - int wlsw; - - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { - mutex_lock(&tpacpi_inputdev_send_mutex); - - input_report_switch(tpacpi_inputdev, - SW_RFKILL_ALL, !!wlsw); - input_sync(tpacpi_inputdev); - - mutex_unlock(&tpacpi_inputdev_send_mutex); - } -} - static void tpacpi_input_send_tabletsw(void) { int state; @@ -1921,6 +1906,22 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_wakeup_hotunplug_complete.attr, }; +static void tpacpi_send_radiosw_update(void) +{ + int wlsw; + + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { + mutex_lock(&tpacpi_inputdev_send_mutex); + + input_report_switch(tpacpi_inputdev, + SW_RFKILL_ALL, !!wlsw); + input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); + } + hotkey_radio_sw_notify_change(); +} + static void hotkey_exit(void) { #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL @@ -2288,7 +2289,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) tpacpi_inputdev->close = &hotkey_inputdev_close; hotkey_poll_setup_safe(1); - tpacpi_input_send_radiosw(); + tpacpi_send_radiosw_update(); tpacpi_input_send_tabletsw(); return 0; @@ -2420,8 +2421,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) case 7: /* 0x7000-0x7FFF: misc */ if (tp_features.hotkey_wlsw && hkey == 0x7000) { - tpacpi_input_send_radiosw(); - hotkey_radio_sw_notify_change(); + tpacpi_send_radiosw_update(); send_acpi_ev = 0; break; } @@ -2464,8 +2464,7 @@ static void hotkey_resume(void) printk(TPACPI_ERR "error while trying to read hot key mask " "from firmware\n"); - tpacpi_input_send_radiosw(); - hotkey_radio_sw_notify_change(); + tpacpi_send_radiosw_update(); hotkey_tablet_mode_notify_change(); hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); -- cgit v1.2.3-58-ga151 From 07431ec82bf9dc74b470a1d820b41c92c4d86e6f Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:50 -0300 Subject: ACPI: thinkpad-acpi: prepare for bluetooth and wwan rfkill support Get rid of some forward definitions by moving code around, this will make the rfkill conversion of wwan and bluetooth a bit cleaner. Signed-off-by: Henrique de Moraes Holschuh --- drivers/misc/thinkpad_acpi.c | 154 +++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 80 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 9179f2367d42..743a4d6098e8 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2581,8 +2581,37 @@ enum { TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ }; -static int bluetooth_get_radiosw(void); -static int bluetooth_set_radiosw(int radio_on); +static int bluetooth_get_radiosw(void) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + + return (status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0; +} + +static int bluetooth_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_BLUETOOTH_RADIOSSW; + else + status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) + return -EIO; + + return 0; +} /* sysfs bluetooth enable ---------------------------------------------- */ static ssize_t bluetooth_enable_show(struct device *dev, @@ -2628,6 +2657,12 @@ static const struct attribute_group bluetooth_attr_group = { .attrs = bluetooth_attributes, }; +static void bluetooth_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); +} + static int __init bluetooth_init(struct ibm_init_struct *iibm) { int res; @@ -2664,44 +2699,6 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) return (tp_features.bluetooth)? 0 : 1; } -static void bluetooth_exit(void) -{ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, - &bluetooth_attr_group); -} - -static int bluetooth_get_radiosw(void) -{ - int status; - - if (!tp_features.bluetooth) - return -ENODEV; - - if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) - return -EIO; - - return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); -} - -static int bluetooth_set_radiosw(int radio_on) -{ - int status; - - if (!tp_features.bluetooth) - return -ENODEV; - - if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) - return -EIO; - if (radio_on) - status |= TP_ACPI_BLUETOOTH_RADIOSSW; - else - status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; - if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) - return -EIO; - - return 0; -} - /* procfs -------------------------------------------------------------- */ static int bluetooth_read(char *p) { @@ -2756,8 +2753,37 @@ enum { TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ }; -static int wan_get_radiosw(void); -static int wan_set_radiosw(int radio_on); +static int wan_get_radiosw(void) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + + return (status & TP_ACPI_WANCARD_RADIOSSW) != 0; +} + +static int wan_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_WANCARD_RADIOSSW; + else + status &= ~TP_ACPI_WANCARD_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) + return -EIO; + + return 0; +} /* sysfs wan enable ---------------------------------------------------- */ static ssize_t wan_enable_show(struct device *dev, @@ -2803,6 +2829,12 @@ static const struct attribute_group wan_attr_group = { .attrs = wan_attributes, }; +static void wan_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); +} + static int __init wan_init(struct ibm_init_struct *iibm) { int res; @@ -2837,44 +2869,6 @@ static int __init wan_init(struct ibm_init_struct *iibm) return (tp_features.wan)? 0 : 1; } -static void wan_exit(void) -{ - sysfs_remove_group(&tpacpi_pdev->dev.kobj, - &wan_attr_group); -} - -static int wan_get_radiosw(void) -{ - int status; - - if (!tp_features.wan) - return -ENODEV; - - if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) - return -EIO; - - return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); -} - -static int wan_set_radiosw(int radio_on) -{ - int status; - - if (!tp_features.wan) - return -ENODEV; - - if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) - return -EIO; - if (radio_on) - status |= TP_ACPI_WANCARD_RADIOSSW; - else - status &= ~TP_ACPI_WANCARD_RADIOSSW; - if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) - return -EIO; - - return 0; -} - /* procfs -------------------------------------------------------------- */ static int wan_read(char *p) { -- cgit v1.2.3-58-ga151 From 133ec3bd3ae409895eacdce326cdc8d73c249e8a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:50 -0300 Subject: ACPI: thinkpad-acpi: WLSW overrides other rfkill switches On ThinkPads where the WLSW switch exists, the firmware or the hardware ANDs the WLSW state with the device-specific switches (WWAN, Bluetooth). It is downright impossible to enable WWAN or Bluetooth when WLSW is blocking the radios. This reality does not necessarily carry over to the WWAN and Bluetooth firmware interfaces, though... so the state thinkpad-acpi was reporting could be incorrect. Tie the three switches in the driver so that we keep their state sane. When WLSL is off, force the other switches to off as well. Signed-off-by: Henrique de Moraes Holschuh --- drivers/misc/thinkpad_acpi.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 743a4d6098e8..202d63e1b391 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -2588,6 +2588,10 @@ static int bluetooth_get_radiosw(void) if (!tp_features.bluetooth) return -ENODEV; + /* WLSW overrides bluetooth in firmware/hardware, reflect that */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) + return 0; + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) return -EIO; @@ -2601,6 +2605,12 @@ static int bluetooth_set_radiosw(int radio_on) if (!tp_features.bluetooth) return -ENODEV; + /* WLSW overrides bluetooth in firmware/hardware, but there is no + * reason to risk weird behaviour. */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status + && radio_on) + return -EPERM; + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) return -EIO; if (radio_on) @@ -2760,6 +2770,10 @@ static int wan_get_radiosw(void) if (!tp_features.wan) return -ENODEV; + /* WLSW overrides WWAN in firmware/hardware, reflect that */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) + return 0; + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) return -EIO; @@ -2773,6 +2787,12 @@ static int wan_set_radiosw(int radio_on) if (!tp_features.wan) return -ENODEV; + /* WLSW overrides bluetooth in firmware/hardware, but there is no + * reason to risk weird behaviour. */ + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status + && radio_on) + return -EPERM; + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) return -EIO; if (radio_on) -- cgit v1.2.3-58-ga151 From 0e74dc2646db04b644faa8ea10ff4f408d55cf90 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:51 -0300 Subject: ACPI: thinkpad-acpi: add bluetooth and WWAN rfkill support Add a read/write rfkill interface to the bluetooth radio switch on the bluetooth submodule, and one for the wireless wan radio switch to the wan submodule. Since rfkill does care for when a switch changes state, use WLSW notifications to also check if the WWAN or Bluetooth switches did not change state (due to them being slaves of WLSW in firmware/hardware, but that reality not being always properly exported by the thinkpad firmware). Signed-off-by: Henrique de Moraes Holschuh Cc: Ivo van Doorn Cc: John W. Linville --- Documentation/laptops/thinkpad-acpi.txt | 22 +++- drivers/misc/Kconfig | 2 + drivers/misc/thinkpad_acpi.c | 208 ++++++++++++++++++++++++++++---- 3 files changed, 200 insertions(+), 32 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 64b3f146e4b0..1c1c0217ebd1 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -621,7 +621,8 @@ Bluetooth --------- procfs: /proc/acpi/ibm/bluetooth -sysfs device attribute: bluetooth_enable +sysfs device attribute: bluetooth_enable (deprecated) +sysfs rfkill class: switch "tpacpi_bluetooth_sw" This feature shows the presence and current state of a ThinkPad Bluetooth device in the internal ThinkPad CDC slot. @@ -643,8 +644,12 @@ Sysfs notes: 0: disables Bluetooth / Bluetooth is disabled 1: enables Bluetooth / Bluetooth is enabled. - Note: this interface will be probably be superseded by the - generic rfkill class, so it is NOT to be considered stable yet. + Note: this interface has been superseded by the generic rfkill + class. It has been deprecated, and it will be removed in year + 2010. + + rfkill controller switch "tpacpi_bluetooth_sw": refer to + Documentation/rfkill.txt for details. Video output control -- /proc/acpi/ibm/video -------------------------------------------- @@ -1374,7 +1379,8 @@ EXPERIMENTAL: WAN ----------------- procfs: /proc/acpi/ibm/wan -sysfs device attribute: wwan_enable +sysfs device attribute: wwan_enable (deprecated) +sysfs rfkill class: switch "tpacpi_wwan_sw" This feature is marked EXPERIMENTAL because the implementation directly accesses hardware registers and may not work as expected. USE @@ -1404,8 +1410,12 @@ Sysfs notes: 0: disables WWAN card / WWAN card is disabled 1: enables WWAN card / WWAN card is enabled. - Note: this interface will be probably be superseded by the - generic rfkill class, so it is NOT to be considered stable yet. + Note: this interface has been superseded by the generic rfkill + class. It has been deprecated, and it will be removed in year + 2010. + + rfkill controller switch "tpacpi_wwan_sw": refer to + Documentation/rfkill.txt for details. Multiple Commands, Module Parameters ------------------------------------ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1921b8dbb242..b27ca91fd15e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -279,6 +279,8 @@ config THINKPAD_ACPI select INPUT select NEW_LEDS select LEDS_CLASS + select NET + select RFKILL ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 202d63e1b391..dc8d00a45701 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -68,6 +68,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,12 @@ enum { #define TPACPI_MAX_ACPI_ARGS 3 +/* rfkill switches */ +enum { + TPACPI_RFK_BLUETOOTH_SW_ID = 0, + TPACPI_RFK_WWAN_SW_ID, +}; + /* Debugging */ #define TPACPI_LOG TPACPI_FILE ": " #define TPACPI_ERR KERN_ERR TPACPI_LOG @@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) return 0; } +static int __init tpacpi_new_rfkill(const unsigned int id, + struct rfkill **rfk, + const enum rfkill_type rfktype, + const char *name, + int (*toggle_radio)(void *, enum rfkill_state), + int (*get_state)(void *, enum rfkill_state *)) +{ + int res; + enum rfkill_state initial_state; + + *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); + if (!*rfk) { + printk(TPACPI_ERR + "failed to allocate memory for rfkill class\n"); + return -ENOMEM; + } + + (*rfk)->name = name; + (*rfk)->get_state = get_state; + (*rfk)->toggle_radio = toggle_radio; + + if (!get_state(NULL, &initial_state)) + (*rfk)->state = initial_state; + + res = rfkill_register(*rfk); + if (res < 0) { + printk(TPACPI_ERR + "failed to register %s rfkill switch: %d\n", + name, res); + rfkill_free(*rfk); + *rfk = NULL; + return res; + } + + return 0; +} + /************************************************************************* * thinkpad-acpi driver attributes */ @@ -1906,10 +1950,18 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_wakeup_hotunplug_complete.attr, }; +static void bluetooth_update_rfk(void); +static void wan_update_rfk(void); static void tpacpi_send_radiosw_update(void) { int wlsw; + /* Sync these BEFORE sending any rfkill events */ + if (tp_features.bluetooth) + bluetooth_update_rfk(); + if (tp_features.wan) + wan_update_rfk(); + if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { mutex_lock(&tpacpi_inputdev_send_mutex); @@ -2581,6 +2633,8 @@ enum { TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ }; +static struct rfkill *tpacpi_bluetooth_rfkill; + static int bluetooth_get_radiosw(void) { int status; @@ -2590,15 +2644,29 @@ static int bluetooth_get_radiosw(void) /* WLSW overrides bluetooth in firmware/hardware, reflect that */ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) - return 0; + return RFKILL_STATE_HARD_BLOCKED; if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) return -EIO; - return (status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0; + return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; } -static int bluetooth_set_radiosw(int radio_on) +static void bluetooth_update_rfk(void) +{ + int status; + + if (!tpacpi_bluetooth_rfkill) + return; + + status = bluetooth_get_radiosw(); + if (status < 0) + return; + rfkill_force_state(tpacpi_bluetooth_rfkill, status); +} + +static int bluetooth_set_radiosw(int radio_on, int update_rfk) { int status; @@ -2620,6 +2688,9 @@ static int bluetooth_set_radiosw(int radio_on) if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) return -EIO; + if (update_rfk) + bluetooth_update_rfk(); + return 0; } @@ -2634,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, if (status < 0) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%d\n", + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); } static ssize_t bluetooth_enable_store(struct device *dev, @@ -2647,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; - res = bluetooth_set_radiosw(t); + res = bluetooth_set_radiosw(t, 1); return (res) ? res : count; } @@ -2667,8 +2739,27 @@ static const struct attribute_group bluetooth_attr_group = { .attrs = bluetooth_attributes, }; +static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) +{ + int bts = bluetooth_get_radiosw(); + + if (bts < 0) + return bts; + + *state = bts; + return 0; +} + +static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) +{ + return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); +} + static void bluetooth_exit(void) { + if (tpacpi_bluetooth_rfkill) + rfkill_unregister(tpacpi_bluetooth_rfkill); + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &bluetooth_attr_group); } @@ -2699,14 +2790,26 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) "bluetooth hardware not installed\n"); } - if (tp_features.bluetooth) { - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + if (!tp_features.bluetooth) + return 1; + + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &bluetooth_attr_group); - if (res) - return res; + if (res) + return res; + + res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, + &tpacpi_bluetooth_rfkill, + RFKILL_TYPE_BLUETOOTH, + "tpacpi_bluetooth_sw", + tpacpi_bluetooth_rfk_set, + tpacpi_bluetooth_rfk_get); + if (res) { + bluetooth_exit(); + return res; } - return (tp_features.bluetooth)? 0 : 1; + return 0; } /* procfs -------------------------------------------------------------- */ @@ -2719,7 +2822,8 @@ static int bluetooth_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); else { len += sprintf(p + len, "status:\t\t%s\n", - (status)? "enabled" : "disabled"); + (status == RFKILL_STATE_UNBLOCKED) ? + "enabled" : "disabled"); len += sprintf(p + len, "commands:\tenable, disable\n"); } @@ -2735,9 +2839,9 @@ static int bluetooth_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - bluetooth_set_radiosw(1); + bluetooth_set_radiosw(1, 1); } else if (strlencmp(cmd, "disable") == 0) { - bluetooth_set_radiosw(0); + bluetooth_set_radiosw(0, 1); } else return -EINVAL; } @@ -2763,6 +2867,8 @@ enum { TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ }; +static struct rfkill *tpacpi_wan_rfkill; + static int wan_get_radiosw(void) { int status; @@ -2772,15 +2878,29 @@ static int wan_get_radiosw(void) /* WLSW overrides WWAN in firmware/hardware, reflect that */ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) - return 0; + return RFKILL_STATE_HARD_BLOCKED; if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) return -EIO; - return (status & TP_ACPI_WANCARD_RADIOSSW) != 0; + return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? + RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; } -static int wan_set_radiosw(int radio_on) +static void wan_update_rfk(void) +{ + int status; + + if (!tpacpi_wan_rfkill) + return; + + status = wan_get_radiosw(); + if (status < 0) + return; + rfkill_force_state(tpacpi_wan_rfkill, status); +} + +static int wan_set_radiosw(int radio_on, int update_rfk) { int status; @@ -2802,6 +2922,9 @@ static int wan_set_radiosw(int radio_on) if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) return -EIO; + if (update_rfk) + wan_update_rfk(); + return 0; } @@ -2816,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev, if (status < 0) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%d\n", + (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); } static ssize_t wan_enable_store(struct device *dev, @@ -2829,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; - res = wan_set_radiosw(t); + res = wan_set_radiosw(t, 1); return (res) ? res : count; } @@ -2849,8 +2973,27 @@ static const struct attribute_group wan_attr_group = { .attrs = wan_attributes, }; +static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) +{ + int wans = wan_get_radiosw(); + + if (wans < 0) + return wans; + + *state = wans; + return 0; +} + +static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) +{ + return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); +} + static void wan_exit(void) { + if (tpacpi_wan_rfkill) + rfkill_unregister(tpacpi_wan_rfkill); + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &wan_attr_group); } @@ -2879,14 +3022,26 @@ static int __init wan_init(struct ibm_init_struct *iibm) "wan hardware not installed\n"); } - if (tp_features.wan) { - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + if (!tp_features.wan) + return 1; + + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &wan_attr_group); - if (res) - return res; + if (res) + return res; + + res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, + &tpacpi_wan_rfkill, + RFKILL_TYPE_WWAN, + "tpacpi_wwan_sw", + tpacpi_wan_rfk_set, + tpacpi_wan_rfk_get); + if (res) { + wan_exit(); + return res; } - return (tp_features.wan)? 0 : 1; + return 0; } /* procfs -------------------------------------------------------------- */ @@ -2899,7 +3054,8 @@ static int wan_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); else { len += sprintf(p + len, "status:\t\t%s\n", - (status)? "enabled" : "disabled"); + (status == RFKILL_STATE_UNBLOCKED) ? + "enabled" : "disabled"); len += sprintf(p + len, "commands:\tenable, disable\n"); } @@ -2915,9 +3071,9 @@ static int wan_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - wan_set_radiosw(1); + wan_set_radiosw(1, 1); } else if (strlencmp(cmd, "disable") == 0) { - wan_set_radiosw(0); + wan_set_radiosw(0, 1); } else return -EINVAL; } -- cgit v1.2.3-58-ga151 From 490673dc98adfc7de1703cc88508902bd10f446b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:51 -0300 Subject: ACPI: thinkpad-acpi: bump up version to 0.21 rfkill support deserves a new version checkpoint... Signed-off-by: Henrique de Moraes Holschuh --- Documentation/laptops/thinkpad-acpi.txt | 4 ++-- drivers/misc/thinkpad_acpi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 1c1c0217ebd1..02dc748b76c4 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.20 - April 09th, 2008 + Version 0.21 + May 29th, 2008 Borislav Deianov Henrique de Moraes Holschuh diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index dc8d00a45701..3eb01afe4306 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -21,7 +21,7 @@ * 02110-1301, USA. */ -#define TPACPI_VERSION "0.20" +#define TPACPI_VERSION "0.21" #define TPACPI_SYSFS_VERSION 0x020200 /* -- cgit v1.2.3-58-ga151 From bf20e740a4bcc686de02e2fd1c1810a58872f46e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 21 Jul 2008 09:15:51 -0300 Subject: ACPI: thinkpad-acpi: don't misdetect in get_thinkpad_model_data() on -ENOMEM Explicitly check for memory allocation failures, and return status to indicate that we could not collect data due to errors. This lets the driver have a far more predictable failure mode on ENOMEM in that codepath: it will refuse to load. This is far better than trying to proceed with missing data which is used to detect quirks, etc. Signed-off-by: Henrique de Moraes Holschuh --- drivers/misc/thinkpad_acpi.c | 47 +++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 3eb01afe4306..d3eb7903c346 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -6340,13 +6340,18 @@ err_out: /* Probing */ -static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) +/* returns 0 - probe ok, or < 0 - probe error. + * Probe ok doesn't mean thinkpad found. + * On error, kfree() cleanup on tp->* is not performed, caller must do it */ +static int __must_check __init get_thinkpad_model_data( + struct thinkpad_id_data *tp) { const struct dmi_device *dev = NULL; char ec_fw_string[18]; + char const *s; if (!tp) - return; + return -EINVAL; memset(tp, 0, sizeof(*tp)); @@ -6355,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) else if (dmi_name_in_vendors("LENOVO")) tp->vendor = PCI_VENDOR_ID_LENOVO; else - return; + return 0; - tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), - GFP_KERNEL); + s = dmi_get_system_info(DMI_BIOS_VERSION); + tp->bios_version_str = kstrdup(s, GFP_KERNEL); + if (s && !tp->bios_version_str) + return -ENOMEM; if (!tp->bios_version_str) - return; + return 0; tp->bios_model = tp->bios_version_str[0] | (tp->bios_version_str[1] << 8); @@ -6379,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); + if (!tp->ec_version_str) + return -ENOMEM; tp->ec_model = ec_fw_string[0] | (ec_fw_string[1] << 8); break; } } - tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), - GFP_KERNEL); - if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { - kfree(tp->model_str); - tp->model_str = NULL; + s = dmi_get_system_info(DMI_PRODUCT_VERSION); + if (s && !strnicmp(s, "ThinkPad", 8)) { + tp->model_str = kstrdup(s, GFP_KERNEL); + if (!tp->model_str) + return -ENOMEM; } - tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), - GFP_KERNEL); + s = dmi_get_system_info(DMI_PRODUCT_NAME); + tp->nummodel_str = kstrdup(s, GFP_KERNEL); + if (s && !tp->nummodel_str) + return -ENOMEM; + + return 0; } static int __init probe_for_thinkpad(void) @@ -6656,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void) /* Driver-level probe */ - get_thinkpad_model_data(&thinkpad_id); + ret = get_thinkpad_model_data(&thinkpad_id); + if (ret) { + printk(TPACPI_ERR + "unable to get DMI data: %d\n", ret); + thinkpad_acpi_module_exit(); + return ret; + } ret = probe_for_thinkpad(); if (ret) { thinkpad_acpi_module_exit(); -- cgit v1.2.3-58-ga151 From f88133d76ea38761b7379d6233b752ed82250a4a Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 21 Jul 2008 15:57:45 +0200 Subject: acpi: fix crash in core ACPI code, triggered by CONFIG_ACPI_PCI_SLOT=y -tip testing found the following boot crash on 32-bit x86 (Core2Duo laptop) yesterday: [ 5.606664] scsi4 : ata_piix [ 5.606664] scsi5 : ata_piix [ 5.606664] ACPI Error (psargs-0358): [\_SB_.PCI0.LPC_.EC__.BSTA] Namespace lookup failure, AE_NOT_FOUND [ 5.606664] ACPI Error (psparse-0530): ACPI Error (nsnames-0186): Invalid NS Node (f7c0e960) while traversing path [20080609] [ 5.606664] BUG: unable to handle kernel NULL pointer dereference at 0000000f [ 5.606664] IP: [<80339e2f>] acpi_ns_build_external_path+0x1f/0x80 [ 5.609997] *pdpt = 0000000000a03001 *pde = 0000000000000000 [ 5.609997] Oops: 0002 [#1] SMP [ 5.609997] [ 5.609997] Pid: 1, comm: swapper Not tainted (2.6.26-tip-03965-gbbfb62e-dirty #3153) [ 5.609997] EIP: 0060:[<80339e2f>] EFLAGS: 00010286 CPU: 0 [ 5.609997] EIP is at acpi_ns_build_external_path+0x1f/0x80 [ 5.609997] EAX: f7c18c18 EBX: ffffffff ECX: 00000010 EDX: 00000000 [ 5.609997] ESI: f7c18c18 EDI: 00000010 EBP: f7c4dc28 ESP: f7c4dc18 [ 5.609997] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [ 5.609997] Process swapper (pid: 1, ti=f7c4c000 task=f7c50000 task.ti=f7c4c000) [ 5.609997] Stack: 00000000 00000000 f7c18c18 f7c4dc48 f7c4dc40 80339ed0 00000000 f7c18c18 [ 5.609997] 8084c1b6 8084c1b6 f7c4dc58 8033a60a 00000000 00000010 00000000 f7c18c18 [ 5.609997] f7c4dc70 8033a68f f7c18c18 00000000 f6de7600 00000005 f7c4dc98 8033c34d [ 5.609997] Call Trace: [ 5.609997] [<80339ed0>] ? acpi_ns_handle_to_pathname+0x40/0x72 [ 5.609997] [<8033a60a>] ? acpi_ns_print_node_pathname+0x2c/0x61 [ 5.609997] [<8033a68f>] ? acpi_ns_report_method_error+0x50/0x6d [ 5.609997] [<8033c34d>] ? acpi_ps_parse_aml+0x149/0x2f9 [ 5.609997] [<8033d6dd>] ? acpi_ps_execute_method+0x132/0x201 [ 5.609997] [<80339d19>] ? acpi_ns_evaluate+0x1ad/0x258 [ 5.609997] [<803406c4>] ? acpi_ut_evaluate_object+0x55/0x18f [ 5.609997] [<803408b7>] ? acpi_ut_execute_STA+0x22/0x7a [ 5.609997] [<8033a907>] ? acpi_get_object_info+0x131/0x1be [ 5.609997] [<80344bb2>] ? do_acpi_find_child+0x22/0x4b [ 5.609997] [<8033b855>] ? acpi_ns_walk_namespace+0xa5/0x124 [ 5.609997] [<803394f3>] ? acpi_walk_namespace+0x54/0x74 [ 5.609997] [<80344b90>] ? do_acpi_find_child+0x0/0x4b [ 5.609997] [<80344b85>] ? acpi_get_child+0x38/0x43 [ 5.609997] [<80344b90>] ? do_acpi_find_child+0x0/0x4b [ 5.609997] [<804d0148>] ? ata_acpi_associate+0xb5/0x1b5 [ 5.609997] [<804c6ecb>] ? ata_scsi_add_hosts+0x8e/0xdc [ 5.609997] [<804c40c8>] ? ata_host_register+0x9f/0x1d6 [ 5.609997] [<804cbc7f>] ? ata_pci_sff_activate_host+0x179/0x19f [ 5.609997] [<804cdd45>] ? ata_sff_interrupt+0x0/0x1c7 [ 5.609997] [<8069b033>] ? piix_init_one+0x569/0x5b0 [ 5.609997] [<801bd400>] ? sysfs_ilookup_test+0x0/0x11 [ 5.609997] [<801987d7>] ? ilookup5_nowait+0x29/0x30 [ 5.609997] [<802efc7e>] ? pci_match_device+0x99/0xa3 [ 5.609997] [<802efd3c>] ? pci_device_probe+0x39/0x59 [ 5.609997] [<803bc4af>] ? driver_probe_device+0xa0/0x11b [ 5.609997] [<803bc564>] ? __driver_attach+0x3a/0x59 [ 5.609997] [<803bbde3>] ? bus_for_each_dev+0x36/0x58 [ 5.609997] [<803bc354>] ? driver_attach+0x14/0x16 [ 5.609997] [<803bc52a>] ? __driver_attach+0x0/0x59 [ 5.609997] [<803bc161>] ? bus_add_driver+0x93/0x196 [ 5.609997] [<803bc773>] ? driver_register+0x71/0xcd [ 5.609997] [<802eff05>] ? __pci_register_driver+0x3f/0x6e [ 5.609997] [<809af7ff>] ? piix_init+0x14/0x24 [ 5.609997] [<80984568>] ? kernel_init+0x128/0x269 [ 5.609997] [<809af7eb>] ? piix_init+0x0/0x24 [ 5.609997] [<802e2758>] ? trace_hardirqs_on_thunk+0xc/0x10 [ 5.609997] [<80116aef>] ? restore_nocheck_notrace+0x0/0xe [ 5.609997] [<80984440>] ? kernel_init+0x0/0x269 [ 5.609997] [<80984440>] ? kernel_init+0x0/0x269 [ 5.609997] [<80117d87>] ? kernel_thread_helper+0x7/0x10 [ 5.609997] ======================= [ 5.609997] Code: 75 02 b3 01 8d 43 01 8b 5d fc c9 c3 55 89 e5 57 89 cf 56 53 89 d3 4b 83 ec 04 83 fb 03 89 55 f0 77 09 c6 01 5c c6 41 01 00 eb 59 04 19 00 8b 55 f0 8d 34 11 89 c2 eb 19 8b 42 08 83 eb 05 89 [ 5.609997] EIP: [<80339e2f>] acpi_ns_build_external_path+0x1f/0x80 SS:ESP 0068:f7c4dc18 [ 5.613331] Kernel panic - not syncing: Fatal exception [ 5.613331] Rebooting in 1 seconds..[ 4.646664] ata1: SATA link up 1.5 Gbps (SStatus 113 SControl 300) I have bisected it down to: # bad: [5b664cbe] Merge branch 'upstream-linus' of git://git.kernel. # good: [bce7f795] Linux 2.6.26 # good: [e18425ab] Merge branch 'tracing/for-linus' of git://git.kern # good: [cadc7236] Merge branch 'bkl-removal' into next # good: [4515889a] Merge branch 'merge' of git://git.kernel.org/pub/s # good: [42fdd14e] Merge git://git.kernel.org/pub/scm/linux/kernel/gi # good: [8a0ca91f] Merge branch 'for-linus' of git://git.kernel.org/p # bad: [0af4b8cb] ACPI: Introduce new device wakeup flag 'prepared' # good: [fe997407] PCI: construct one fakephp slot per PCI slot # bad: [531f254a] PCIE: aer: use dev_printk when possible # bad: [15650a20] x86/PCI: fixup early quirk probing # good: [0e6859d9] ACPI PM: Remove obsolete Toshiba workaround # bad: [8344b566] PCI: ACPI PCI slot detection driver # good: [f46753c9] PCI: introduce pci_slot | 8344b568f5bdc7ee1bba909de3294c6348c36056 is first bad commit | commit 8344b568f5bdc7ee1bba909de3294c6348c36056 | Author: Alex Chiang | Date: Tue Jun 10 15:30:42 2008 -0600 | | PCI: ACPI PCI slot detection driver | | Detect all physical PCI slots as described by ACPI, and create entries in | /sys/bus/pci/slots/. I.e. the new CONFIG_ACPI_PCI_SLOT=y option was causing this crash. But the bug is not mainly in this new PCI code - that code was just hitting the ACPI code in a new way which made ACPI break. The crash signature shows that we are crashing on this instruction: movb $0x0, (%ecx, %ebx, 1) ECX and EBX are 0x10 and -1. It's this line in drivers/acpi/namespace/nsnames.c's acpi_ns_build_external_path(): name_buffer[index] = 0; I.e. name_buffer is 0x10 and index is -1. index -1 corresponds to size 0, and name_buffer 0x10 is slab's ZERO_SIZE_PTR special-case for zero-sized allocations. I.e. when we called acpi_ns_handle_to_pathname(), we got required_size of 0 due to an error condition, but this is passed to the ACPI allocator unconditionally: required_size = acpi_ns_get_pathname_length(node); /* Validate/Allocate/Clear caller buffer */ status = acpi_ut_initialize_buffer(buffer, required_size); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } Where acpi_ut_initialize_buffer(), through many (unnecessary) layers, ends up calling kzalloc(0). Which returns 0x10 and that then causes the crash later on. So fix both callers of acpi_ns_get_pathname_length(), which can return 0 in case of an invalid node. Also add a WARN_ON() against zero sized allocations in acpi_ut_initialize_buffer() to make it easier to find similar instances of this bug. I have tested this patch for the past 24 hours and the crash has not reappeared. Signed-off-by: Ingo Molnar Signed-off-by: Andi Kleen --- drivers/acpi/namespace/nsnames.c | 8 ++++++++ drivers/acpi/utilities/utalloc.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c index cffef1bcbdbc..549db42f16cf 100644 --- a/drivers/acpi/namespace/nsnames.c +++ b/drivers/acpi/namespace/nsnames.c @@ -137,6 +137,10 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) /* Calculate required buffer size based on depth below root */ size = acpi_ns_get_pathname_length(node); + if (!size) { + ACPI_ERROR((AE_INFO, "Invalid node failure")); + return_PTR(NULL); + } /* Allocate a buffer to be returned to caller */ @@ -229,6 +233,10 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle, /* Determine size required for the caller buffer */ required_size = acpi_ns_get_pathname_length(node); + if (!required_size) { + ACPI_ERROR((AE_INFO, "Invalid node failure")); + return_ACPI_STATUS(AE_ERROR); + } /* Validate/Allocate/Clear caller buffer */ diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c index 3dfb8a442b26..e7bf34a7b1d2 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/utilities/utalloc.c @@ -242,6 +242,10 @@ acpi_ut_initialize_buffer(struct acpi_buffer * buffer, { acpi_status status = AE_OK; + if (!required_length) { + WARN_ON(1); + return AE_ERROR; + } switch (buffer->length) { case ACPI_NO_BUFFER: -- cgit v1.2.3-58-ga151