Merge branch 'release-2.6.27' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-acpi-2.6

* 'release-2.6.27' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-acpi-2.6:
  acpi: fix crash in core ACPI code, triggered by CONFIG_ACPI_PCI_SLOT=y
  ACPI: thinkpad-acpi: don't misdetect in get_thinkpad_model_data() on -ENOMEM
  ACPI: thinkpad-acpi: bump up version to 0.21
  ACPI: thinkpad-acpi: add bluetooth and WWAN rfkill support
  ACPI: thinkpad-acpi: WLSW overrides other rfkill switches
  ACPI: thinkpad-acpi: prepare for bluetooth and wwan rfkill support
  ACPI: thinkpad-acpi: consolidate wlsw notification function
  ACPI: thinkpad-acpi: minor refactor on radio switch init
  Revert "ACPI: don't walk tables if ACPI was disabled"
  Revert "dock: bay: Don't call acpi_walk_namespace() when ACPI is disabled."
  Revert "Fix FADT parsing"
  ACPI : Set FAN device to correct state in boot phase
  ACPI: Ignore _BQC object when registering backlight device
  ACPI: stop complaints about interrupt link End Tags and blank IRQ descriptors
This commit is contained in:
Linus Torvalds 2008-07-24 13:57:37 -07:00
Родитель 5042d99795 f88133d76e
Коммит 1481b9109f
9 изменённых файлов: 446 добавлений и 174 удалений

Просмотреть файл

@ -1,7 +1,7 @@
ThinkPad ACPI Extras Driver ThinkPad ACPI Extras Driver
Version 0.20 Version 0.21
April 09th, 2008 May 29th, 2008
Borislav Deianov <borislav@users.sf.net> Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br> Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@ -621,7 +621,8 @@ Bluetooth
--------- ---------
procfs: /proc/acpi/ibm/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 This feature shows the presence and current state of a ThinkPad
Bluetooth device in the internal ThinkPad CDC slot. Bluetooth device in the internal ThinkPad CDC slot.
@ -643,8 +644,12 @@ Sysfs notes:
0: disables Bluetooth / Bluetooth is disabled 0: disables Bluetooth / Bluetooth is disabled
1: enables Bluetooth / Bluetooth is enabled. 1: enables Bluetooth / Bluetooth is enabled.
Note: this interface will be probably be superseded by the Note: this interface has been superseded by the generic rfkill
generic rfkill class, so it is NOT to be considered stable yet. 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 Video output control -- /proc/acpi/ibm/video
-------------------------------------------- --------------------------------------------
@ -1374,7 +1379,8 @@ EXPERIMENTAL: WAN
----------------- -----------------
procfs: /proc/acpi/ibm/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 This feature is marked EXPERIMENTAL because the implementation
directly accesses hardware registers and may not work as expected. USE 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 0: disables WWAN card / WWAN card is disabled
1: enables WWAN card / WWAN card is enabled. 1: enables WWAN card / WWAN card is enabled.
Note: this interface will be probably be superseded by the Note: this interface has been superseded by the generic rfkill
generic rfkill class, so it is NOT to be considered stable yet. 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 Multiple Commands, Module Parameters
------------------------------------ ------------------------------------

Просмотреть файл

@ -137,6 +137,10 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node)
/* Calculate required buffer size based on depth below root */ /* Calculate required buffer size based on depth below root */
size = acpi_ns_get_pathname_length(node); 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 */ /* 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 */ /* Determine size required for the caller buffer */
required_size = acpi_ns_get_pathname_length(node); 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 */ /* Validate/Allocate/Clear caller buffer */

Просмотреть файл

@ -113,20 +113,23 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context)
switch (resource->type) { switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT: case ACPI_RESOURCE_TYPE_START_DEPENDENT:
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK; return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ: case ACPI_RESOURCE_TYPE_IRQ:
{ {
struct acpi_resource_irq *p = &resource->data.irq; struct acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->interrupt_count) { 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; return AE_OK;
} }
for (i = 0; for (i = 0;
(i < p->interrupt_count (i < p->interrupt_count
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) { if (!p->interrupts[i]) {
printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", printk(KERN_WARNING PREFIX
p->interrupts[i]); "Invalid _PRS IRQ %d\n",
p->interrupts[i]);
continue; continue;
} }
link->irq.possible[i] = p->interrupts[i]; 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; &resource->data.extended_irq;
if (!p || !p->interrupt_count) { if (!p || !p->interrupt_count) {
printk(KERN_WARNING PREFIX printk(KERN_WARNING PREFIX
"Blank EXT IRQ resource\n"); "Blank _PRS EXT IRQ resource\n");
return AE_OK; return AE_OK;
} }
for (i = 0; for (i = 0;
(i < p->interrupt_count (i < p->interrupt_count
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) { if (!p->interrupts[i]) {
printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", printk(KERN_WARNING PREFIX
p->interrupts[i]); "Invalid _PRS IRQ %d\n",
p->interrupts[i]);
continue; continue;
} }
link->irq.possible[i] = p->interrupts[i]; link->irq.possible[i] = p->interrupts[i];
@ -163,7 +167,8 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context)
break; break;
} }
default: 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; return AE_OK;
} }
@ -199,6 +204,9 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
switch (resource->type) { switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ: case ACPI_RESOURCE_TYPE_IRQ:
{ {
struct acpi_resource_irq *p = &resource->data.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 * particularly those those w/ _STA disabled
*/ */
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Blank IRQ resource\n")); "Blank _CRS IRQ resource\n"));
return AE_OK; return AE_OK;
} }
*irq = p->interrupts[0]; *irq = p->interrupts[0];
@ -224,7 +232,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
* return at least 1 IRQ * return at least 1 IRQ
*/ */
printk(KERN_WARNING PREFIX printk(KERN_WARNING PREFIX
"Blank EXT IRQ resource\n"); "Blank _CRS EXT IRQ resource\n");
return AE_OK; return AE_OK;
} }
*irq = p->interrupts[0]; *irq = p->interrupts[0];
@ -232,10 +240,11 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
} }
break; break;
default: default:
printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type); printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n",
case ACPI_RESOURCE_TYPE_END_TAG: resource->type);
return AE_OK; return AE_OK;
} }
return AE_CTRL_TERMINATE; return AE_CTRL_TERMINATE;
} }

Просмотреть файл

@ -124,7 +124,7 @@ static struct acpi_fadt_info fadt_info_table[] = {
static void inline static void inline
acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, 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 */ /* All other fields are byte-wide */
generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; 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->bit_offset = 0;
generic_address->access_width = 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 * The PM event blocks are split into two register blocks, first is the
* PM Status Register block, followed immediately by the PM Enable Register * 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_2(acpi_gbl_FADT.pm1_event_length);
pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT
.xpm1a_event_block.bit_width);
/* The PM1A register block is required */ /* 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 */ /* The PM1B register block is optional, ignore if not present */
if (acpi_gbl_FADT.xpm1b_event_block.address) { 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, acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable,
pm1_register_length, pm1_register_length,
(acpi_gbl_FADT.xpm1b_event_block. (acpi_gbl_FADT.xpm1b_event_block.
address + pm1_register_length)); address + pm1_register_length));
/* Don't forget to copy space_id of the GAS */ /* Don't forget to copy space_id of the GAS */
acpi_gbl_xpm1b_enable.space_id = acpi_gbl_xpm1b_enable.space_id =
acpi_gbl_FADT.xpm1b_event_block.space_id; acpi_gbl_FADT.xpm1a_event_block.space_id;
} }
} }

Просмотреть файл

@ -769,6 +769,47 @@ static void acpi_thermal_run(unsigned long data)
acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)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) static void acpi_thermal_check(void *data)
{ {
int result = 0; int result = 0;
@ -1624,6 +1665,8 @@ static int acpi_thermal_add(struct acpi_device *device)
init_timer(&tz->timer); init_timer(&tz->timer);
acpi_thermal_active_off(tz);
acpi_thermal_check(tz); acpi_thermal_check(tz);
status = acpi_install_notify_handler(device->handle, status = acpi_install_notify_handler(device->handle,

Просмотреть файл

@ -242,6 +242,10 @@ acpi_ut_initialize_buffer(struct acpi_buffer * buffer,
{ {
acpi_status status = AE_OK; acpi_status status = AE_OK;
if (!required_length) {
WARN_ON(1);
return AE_ERROR;
}
switch (buffer->length) { switch (buffer->length) {
case ACPI_NO_BUFFER: case ACPI_NO_BUFFER:

Просмотреть файл

@ -741,7 +741,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
max_level = acpi_video_init_brightness(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; int result;
static int count = 0; static int count = 0;
char *name; char *name;
@ -753,7 +753,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->backlight = backlight_device_register(name, device->backlight = backlight_device_register(name,
NULL, device, &acpi_backlight_ops); NULL, device, &acpi_backlight_ops);
device->backlight->props.max_brightness = device->brightness->count-3; 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); backlight_update_status(device->backlight);
kfree(name); kfree(name);

Просмотреть файл

@ -279,6 +279,8 @@ config THINKPAD_ACPI
select INPUT select INPUT
select NEW_LEDS select NEW_LEDS
select LEDS_CLASS select LEDS_CLASS
select NET
select RFKILL
---help--- ---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video support for Fn-Fx key combinations, Bluetooth control, video

Просмотреть файл

@ -21,7 +21,7 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define TPACPI_VERSION "0.20" #define TPACPI_VERSION "0.21"
#define TPACPI_SYSFS_VERSION 0x020200 #define TPACPI_SYSFS_VERSION 0x020200
/* /*
@ -68,6 +68,7 @@
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/rfkill.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/dmi.h> #include <linux/dmi.h>
@ -144,6 +145,12 @@ enum {
#define TPACPI_MAX_ACPI_ARGS 3 #define TPACPI_MAX_ACPI_ARGS 3
/* rfkill switches */
enum {
TPACPI_RFK_BLUETOOTH_SW_ID = 0,
TPACPI_RFK_WWAN_SW_ID,
};
/* Debugging */ /* Debugging */
#define TPACPI_LOG TPACPI_FILE ": " #define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_ERR KERN_ERR TPACPI_LOG #define TPACPI_ERR KERN_ERR TPACPI_LOG
@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
return 0; 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 * thinkpad-acpi driver attributes
*/ */
@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status)
return 0; 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) static void tpacpi_input_send_tabletsw(void)
{ {
int state; int state;
@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr, &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);
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) static void hotkey_exit(void)
{ {
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
printk(TPACPI_INFO printk(TPACPI_INFO
"radio switch found; radios are %s\n", "radio switch found; radios are %s\n",
enabled(status, 0)); enabled(status, 0));
}
if (tp_features.hotkey_wlsw)
res = add_to_attr_set(hotkey_dev_attributes, res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_radio_sw.attr); &dev_attr_hotkey_radio_sw.attr);
}
/* For X41t, X60t, X61t Tablets... */ /* For X41t, X60t, X61t Tablets... */
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
tpacpi_inputdev->close = &hotkey_inputdev_close; tpacpi_inputdev->close = &hotkey_inputdev_close;
hotkey_poll_setup_safe(1); hotkey_poll_setup_safe(1);
tpacpi_input_send_radiosw(); tpacpi_send_radiosw_update();
tpacpi_input_send_tabletsw(); tpacpi_input_send_tabletsw();
return 0; return 0;
@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
case 7: case 7:
/* 0x7000-0x7FFF: misc */ /* 0x7000-0x7FFF: misc */
if (tp_features.hotkey_wlsw && hkey == 0x7000) { if (tp_features.hotkey_wlsw && hkey == 0x7000) {
tpacpi_input_send_radiosw(); tpacpi_send_radiosw_update();
hotkey_radio_sw_notify_change();
send_acpi_ev = 0; send_acpi_ev = 0;
break; break;
} }
@ -2463,8 +2516,7 @@ static void hotkey_resume(void)
printk(TPACPI_ERR printk(TPACPI_ERR
"error while trying to read hot key mask " "error while trying to read hot key mask "
"from firmware\n"); "from firmware\n");
tpacpi_input_send_radiosw(); tpacpi_send_radiosw_update();
hotkey_radio_sw_notify_change();
hotkey_tablet_mode_notify_change(); hotkey_tablet_mode_notify_change();
hotkey_wakeup_reason_notify_change(); hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change();
@ -2581,8 +2633,66 @@ enum {
TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
}; };
static int bluetooth_get_radiosw(void); static struct rfkill *tpacpi_bluetooth_rfkill;
static int bluetooth_set_radiosw(int radio_on);
static int bluetooth_get_radiosw(void)
{
int status;
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 RFKILL_STATE_HARD_BLOCKED;
if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
return -EIO;
return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
}
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;
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)
status |= TP_ACPI_BLUETOOTH_RADIOSSW;
else
status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
return -EIO;
if (update_rfk)
bluetooth_update_rfk();
return 0;
}
/* sysfs bluetooth enable ---------------------------------------------- */ /* sysfs bluetooth enable ---------------------------------------------- */
static ssize_t bluetooth_enable_show(struct device *dev, static ssize_t bluetooth_enable_show(struct device *dev,
@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
if (status < 0) if (status < 0)
return status; 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, static ssize_t bluetooth_enable_store(struct device *dev,
@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t)) if (parse_strtoul(buf, 1, &t))
return -EINVAL; return -EINVAL;
res = bluetooth_set_radiosw(t); res = bluetooth_set_radiosw(t, 1);
return (res) ? res : count; return (res) ? res : count;
} }
@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = {
.attrs = bluetooth_attributes, .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);
}
static int __init bluetooth_init(struct ibm_init_struct *iibm) static int __init bluetooth_init(struct ibm_init_struct *iibm)
{ {
int res; int res;
@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
str_supported(tp_features.bluetooth), str_supported(tp_features.bluetooth),
status); status);
if (tp_features.bluetooth) { if (tp_features.bluetooth &&
if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
/* no bluetooth hardware present in system */ /* no bluetooth hardware present in system */
tp_features.bluetooth = 0; tp_features.bluetooth = 0;
dbg_printk(TPACPI_DBG_INIT, dbg_printk(TPACPI_DBG_INIT,
"bluetooth hardware not installed\n"); "bluetooth hardware not installed\n");
} else {
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&bluetooth_attr_group);
if (res)
return res;
}
} }
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) if (!tp_features.bluetooth)
return -ENODEV; return 1;
if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
return -EIO; &bluetooth_attr_group);
if (res)
return res;
return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
} &tpacpi_bluetooth_rfkill,
RFKILL_TYPE_BLUETOOTH,
static int bluetooth_set_radiosw(int radio_on) "tpacpi_bluetooth_sw",
{ tpacpi_bluetooth_rfk_set,
int status; tpacpi_bluetooth_rfk_get);
if (res) {
if (!tp_features.bluetooth) bluetooth_exit();
return -ENODEV; return res;
}
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; return 0;
} }
@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n"); len += sprintf(p + len, "status:\t\tnot supported\n");
else { else {
len += sprintf(p + len, "status:\t\t%s\n", 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"); len += sprintf(p + len, "commands:\tenable, disable\n");
} }
@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf)
while ((cmd = next_cmd(&buf))) { while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) { if (strlencmp(cmd, "enable") == 0) {
bluetooth_set_radiosw(1); bluetooth_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) { } else if (strlencmp(cmd, "disable") == 0) {
bluetooth_set_radiosw(0); bluetooth_set_radiosw(0, 1);
} else } else
return -EINVAL; return -EINVAL;
} }
@ -2755,8 +2867,66 @@ enum {
TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
}; };
static int wan_get_radiosw(void); static struct rfkill *tpacpi_wan_rfkill;
static int wan_set_radiosw(int radio_on);
static int wan_get_radiosw(void)
{
int status;
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 RFKILL_STATE_HARD_BLOCKED;
if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
return -EIO;
return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
}
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;
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)
status |= TP_ACPI_WANCARD_RADIOSSW;
else
status &= ~TP_ACPI_WANCARD_RADIOSSW;
if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
return -EIO;
if (update_rfk)
wan_update_rfk();
return 0;
}
/* sysfs wan enable ---------------------------------------------------- */ /* sysfs wan enable ---------------------------------------------------- */
static ssize_t wan_enable_show(struct device *dev, static ssize_t wan_enable_show(struct device *dev,
@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev,
if (status < 0) if (status < 0)
return status; 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, static ssize_t wan_enable_store(struct device *dev,
@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t)) if (parse_strtoul(buf, 1, &t))
return -EINVAL; return -EINVAL;
res = wan_set_radiosw(t); res = wan_set_radiosw(t, 1);
return (res) ? res : count; return (res) ? res : count;
} }
@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = {
.attrs = wan_attributes, .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);
}
static int __init wan_init(struct ibm_init_struct *iibm) static int __init wan_init(struct ibm_init_struct *iibm)
{ {
int res; int res;
@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm)
str_supported(tp_features.wan), str_supported(tp_features.wan),
status); status);
if (tp_features.wan) { if (tp_features.wan &&
if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { !(status & TP_ACPI_WANCARD_HWPRESENT)) {
/* no wan hardware present in system */ /* no wan hardware present in system */
tp_features.wan = 0; tp_features.wan = 0;
dbg_printk(TPACPI_DBG_INIT, dbg_printk(TPACPI_DBG_INIT,
"wan hardware not installed\n"); "wan hardware not installed\n");
} else {
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
&wan_attr_group);
if (res)
return res;
}
} }
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) if (!tp_features.wan)
return -ENODEV; return 1;
if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
return -EIO; &wan_attr_group);
if (res)
return res;
return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
} &tpacpi_wan_rfkill,
RFKILL_TYPE_WWAN,
static int wan_set_radiosw(int radio_on) "tpacpi_wwan_sw",
{ tpacpi_wan_rfk_set,
int status; tpacpi_wan_rfk_get);
if (res) {
if (!tp_features.wan) wan_exit();
return -ENODEV; return res;
}
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; return 0;
} }
@ -2883,7 +3054,8 @@ static int wan_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n"); len += sprintf(p + len, "status:\t\tnot supported\n");
else { else {
len += sprintf(p + len, "status:\t\t%s\n", 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"); len += sprintf(p + len, "commands:\tenable, disable\n");
} }
@ -2899,9 +3071,9 @@ static int wan_write(char *buf)
while ((cmd = next_cmd(&buf))) { while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) { if (strlencmp(cmd, "enable") == 0) {
wan_set_radiosw(1); wan_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) { } else if (strlencmp(cmd, "disable") == 0) {
wan_set_radiosw(0); wan_set_radiosw(0, 1);
} else } else
return -EINVAL; return -EINVAL;
} }
@ -6168,13 +6340,18 @@ err_out:
/* Probing */ /* 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; const struct dmi_device *dev = NULL;
char ec_fw_string[18]; char ec_fw_string[18];
char const *s;
if (!tp) if (!tp)
return; return -EINVAL;
memset(tp, 0, sizeof(*tp)); memset(tp, 0, sizeof(*tp));
@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
else if (dmi_name_in_vendors("LENOVO")) else if (dmi_name_in_vendors("LENOVO"))
tp->vendor = PCI_VENDOR_ID_LENOVO; tp->vendor = PCI_VENDOR_ID_LENOVO;
else else
return; return 0;
tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), s = dmi_get_system_info(DMI_BIOS_VERSION);
GFP_KERNEL); tp->bios_version_str = kstrdup(s, GFP_KERNEL);
if (s && !tp->bios_version_str)
return -ENOMEM;
if (!tp->bios_version_str) if (!tp->bios_version_str)
return; return 0;
tp->bios_model = tp->bios_version_str[0] tp->bios_model = tp->bios_version_str[0]
| (tp->bios_version_str[1] << 8); | (tp->bios_version_str[1] << 8);
@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
if (!tp->ec_version_str)
return -ENOMEM;
tp->ec_model = ec_fw_string[0] tp->ec_model = ec_fw_string[0]
| (ec_fw_string[1] << 8); | (ec_fw_string[1] << 8);
break; break;
} }
} }
tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), s = dmi_get_system_info(DMI_PRODUCT_VERSION);
GFP_KERNEL); if (s && !strnicmp(s, "ThinkPad", 8)) {
if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { tp->model_str = kstrdup(s, GFP_KERNEL);
kfree(tp->model_str); if (!tp->model_str)
tp->model_str = NULL; return -ENOMEM;
} }
tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), s = dmi_get_system_info(DMI_PRODUCT_NAME);
GFP_KERNEL); tp->nummodel_str = kstrdup(s, GFP_KERNEL);
if (s && !tp->nummodel_str)
return -ENOMEM;
return 0;
} }
static int __init probe_for_thinkpad(void) static int __init probe_for_thinkpad(void)
@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void)
/* Driver-level probe */ /* 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(); ret = probe_for_thinkpad();
if (ret) { if (ret) {
thinkpad_acpi_module_exit(); thinkpad_acpi_module_exit();