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:
Коммит
1481b9109f
|
@ -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,19 +113,22 @@ 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
|
||||||
|
"Invalid _PRS IRQ %d\n",
|
||||||
p->interrupts[i]);
|
p->interrupts[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -143,14 +146,15 @@ 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
|
||||||
|
"Invalid _PRS IRQ %d\n",
|
||||||
p->interrupts[i]);
|
p->interrupts[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
}
|
||||||
|
|
||||||
|
if (!tp_features.bluetooth)
|
||||||
|
return 1;
|
||||||
|
|
||||||
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
|
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
|
||||||
&bluetooth_attr_group);
|
&bluetooth_attr_group);
|
||||||
if (res)
|
if (res)
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
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 {
|
}
|
||||||
|
|
||||||
|
if (!tp_features.wan)
|
||||||
|
return 1;
|
||||||
|
|
||||||
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
|
res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
|
||||||
&wan_attr_group);
|
&wan_attr_group);
|
||||||
if (res)
|
if (res)
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
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();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче