diff options
| -rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 26 | ||||
| -rw-r--r-- | drivers/acpi/namespace/nsnames.c | 8 | ||||
| -rw-r--r-- | drivers/acpi/pci_link.c | 31 | ||||
| -rw-r--r-- | drivers/acpi/tables/tbfadt.c | 17 | ||||
| -rw-r--r-- | drivers/acpi/thermal.c | 43 | ||||
| -rw-r--r-- | drivers/acpi/utilities/utalloc.c | 4 | ||||
| -rw-r--r-- | drivers/acpi/video.c | 14 | ||||
| -rw-r--r-- | drivers/misc/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/misc/thinkpad_acpi.c | 475 |
9 files changed, 446 insertions, 174 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 64b3f146e4b0..02dc748b76c4 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | ThinkPad ACPI Extras Driver | 1 | ThinkPad ACPI Extras Driver |
| 2 | 2 | ||
| 3 | Version 0.20 | 3 | Version 0.21 |
| 4 | April 09th, 2008 | 4 | May 29th, 2008 |
| 5 | 5 | ||
| 6 | Borislav Deianov <borislav@users.sf.net> | 6 | Borislav Deianov <borislav@users.sf.net> |
| 7 | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 7 | Henrique de Moraes Holschuh <hmh@hmh.eng.br> |
| @@ -621,7 +621,8 @@ Bluetooth | |||
| 621 | --------- | 621 | --------- |
| 622 | 622 | ||
| 623 | procfs: /proc/acpi/ibm/bluetooth | 623 | procfs: /proc/acpi/ibm/bluetooth |
| 624 | sysfs device attribute: bluetooth_enable | 624 | sysfs device attribute: bluetooth_enable (deprecated) |
| 625 | sysfs rfkill class: switch "tpacpi_bluetooth_sw" | ||
| 625 | 626 | ||
| 626 | This feature shows the presence and current state of a ThinkPad | 627 | This feature shows the presence and current state of a ThinkPad |
| 627 | Bluetooth device in the internal ThinkPad CDC slot. | 628 | Bluetooth device in the internal ThinkPad CDC slot. |
| @@ -643,8 +644,12 @@ Sysfs notes: | |||
| 643 | 0: disables Bluetooth / Bluetooth is disabled | 644 | 0: disables Bluetooth / Bluetooth is disabled |
| 644 | 1: enables Bluetooth / Bluetooth is enabled. | 645 | 1: enables Bluetooth / Bluetooth is enabled. |
| 645 | 646 | ||
| 646 | Note: this interface will be probably be superseded by the | 647 | Note: this interface has been superseded by the generic rfkill |
| 647 | generic rfkill class, so it is NOT to be considered stable yet. | 648 | class. It has been deprecated, and it will be removed in year |
| 649 | 2010. | ||
| 650 | |||
| 651 | rfkill controller switch "tpacpi_bluetooth_sw": refer to | ||
| 652 | Documentation/rfkill.txt for details. | ||
| 648 | 653 | ||
| 649 | Video output control -- /proc/acpi/ibm/video | 654 | Video output control -- /proc/acpi/ibm/video |
| 650 | -------------------------------------------- | 655 | -------------------------------------------- |
| @@ -1374,7 +1379,8 @@ EXPERIMENTAL: WAN | |||
| 1374 | ----------------- | 1379 | ----------------- |
| 1375 | 1380 | ||
| 1376 | procfs: /proc/acpi/ibm/wan | 1381 | procfs: /proc/acpi/ibm/wan |
| 1377 | sysfs device attribute: wwan_enable | 1382 | sysfs device attribute: wwan_enable (deprecated) |
| 1383 | sysfs rfkill class: switch "tpacpi_wwan_sw" | ||
| 1378 | 1384 | ||
| 1379 | This feature is marked EXPERIMENTAL because the implementation | 1385 | This feature is marked EXPERIMENTAL because the implementation |
| 1380 | directly accesses hardware registers and may not work as expected. USE | 1386 | directly accesses hardware registers and may not work as expected. USE |
| @@ -1404,8 +1410,12 @@ Sysfs notes: | |||
| 1404 | 0: disables WWAN card / WWAN card is disabled | 1410 | 0: disables WWAN card / WWAN card is disabled |
| 1405 | 1: enables WWAN card / WWAN card is enabled. | 1411 | 1: enables WWAN card / WWAN card is enabled. |
| 1406 | 1412 | ||
| 1407 | Note: this interface will be probably be superseded by the | 1413 | Note: this interface has been superseded by the generic rfkill |
| 1408 | generic rfkill class, so it is NOT to be considered stable yet. | 1414 | class. It has been deprecated, and it will be removed in year |
| 1415 | 2010. | ||
| 1416 | |||
| 1417 | rfkill controller switch "tpacpi_wwan_sw": refer to | ||
| 1418 | Documentation/rfkill.txt for details. | ||
| 1409 | 1419 | ||
| 1410 | Multiple Commands, Module Parameters | 1420 | Multiple Commands, Module Parameters |
| 1411 | ------------------------------------ | 1421 | ------------------------------------ |
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) | |||
| 137 | /* Calculate required buffer size based on depth below root */ | 137 | /* Calculate required buffer size based on depth below root */ |
| 138 | 138 | ||
| 139 | size = acpi_ns_get_pathname_length(node); | 139 | size = acpi_ns_get_pathname_length(node); |
| 140 | if (!size) { | ||
| 141 | ACPI_ERROR((AE_INFO, "Invalid node failure")); | ||
| 142 | return_PTR(NULL); | ||
| 143 | } | ||
| 140 | 144 | ||
| 141 | /* Allocate a buffer to be returned to caller */ | 145 | /* Allocate a buffer to be returned to caller */ |
| 142 | 146 | ||
| @@ -229,6 +233,10 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle, | |||
| 229 | /* Determine size required for the caller buffer */ | 233 | /* Determine size required for the caller buffer */ |
| 230 | 234 | ||
| 231 | required_size = acpi_ns_get_pathname_length(node); | 235 | required_size = acpi_ns_get_pathname_length(node); |
| 236 | if (!required_size) { | ||
| 237 | ACPI_ERROR((AE_INFO, "Invalid node failure")); | ||
| 238 | return_ACPI_STATUS(AE_ERROR); | ||
| 239 | } | ||
| 232 | 240 | ||
| 233 | /* Validate/Allocate/Clear caller buffer */ | 241 | /* Validate/Allocate/Clear caller buffer */ |
| 234 | 242 | ||
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) | |||
| 113 | 113 | ||
| 114 | switch (resource->type) { | 114 | switch (resource->type) { |
| 115 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: | 115 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: |
| 116 | case ACPI_RESOURCE_TYPE_END_TAG: | ||
| 116 | return AE_OK; | 117 | return AE_OK; |
| 117 | case ACPI_RESOURCE_TYPE_IRQ: | 118 | case ACPI_RESOURCE_TYPE_IRQ: |
| 118 | { | 119 | { |
| 119 | struct acpi_resource_irq *p = &resource->data.irq; | 120 | struct acpi_resource_irq *p = &resource->data.irq; |
| 120 | if (!p || !p->interrupt_count) { | 121 | if (!p || !p->interrupt_count) { |
| 121 | printk(KERN_WARNING PREFIX "Blank IRQ resource\n"); | 122 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
| 123 | "Blank _PRS IRQ resource\n")); | ||
| 122 | return AE_OK; | 124 | return AE_OK; |
| 123 | } | 125 | } |
| 124 | for (i = 0; | 126 | for (i = 0; |
| 125 | (i < p->interrupt_count | 127 | (i < p->interrupt_count |
| 126 | && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { | 128 | && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { |
| 127 | if (!p->interrupts[i]) { | 129 | if (!p->interrupts[i]) { |
| 128 | printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", | 130 | printk(KERN_WARNING PREFIX |
| 129 | p->interrupts[i]); | 131 | "Invalid _PRS IRQ %d\n", |
| 132 | p->interrupts[i]); | ||
| 130 | continue; | 133 | continue; |
| 131 | } | 134 | } |
| 132 | link->irq.possible[i] = p->interrupts[i]; | 135 | link->irq.possible[i] = p->interrupts[i]; |
| @@ -143,15 +146,16 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) | |||
| 143 | &resource->data.extended_irq; | 146 | &resource->data.extended_irq; |
| 144 | if (!p || !p->interrupt_count) { | 147 | if (!p || !p->interrupt_count) { |
| 145 | printk(KERN_WARNING PREFIX | 148 | printk(KERN_WARNING PREFIX |
| 146 | "Blank EXT IRQ resource\n"); | 149 | "Blank _PRS EXT IRQ resource\n"); |
| 147 | return AE_OK; | 150 | return AE_OK; |
| 148 | } | 151 | } |
| 149 | for (i = 0; | 152 | for (i = 0; |
| 150 | (i < p->interrupt_count | 153 | (i < p->interrupt_count |
| 151 | && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { | 154 | && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { |
| 152 | if (!p->interrupts[i]) { | 155 | if (!p->interrupts[i]) { |
| 153 | printk(KERN_WARNING PREFIX "Invalid IRQ %d\n", | 156 | printk(KERN_WARNING PREFIX |
| 154 | p->interrupts[i]); | 157 | "Invalid _PRS IRQ %d\n", |
| 158 | p->interrupts[i]); | ||
| 155 | continue; | 159 | continue; |
| 156 | } | 160 | } |
| 157 | link->irq.possible[i] = p->interrupts[i]; | 161 | link->irq.possible[i] = p->interrupts[i]; |
| @@ -163,7 +167,8 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) | |||
| 163 | break; | 167 | break; |
| 164 | } | 168 | } |
| 165 | default: | 169 | default: |
| 166 | printk(KERN_ERR PREFIX "Resource is not an IRQ entry\n"); | 170 | printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n", |
| 171 | resource->type); | ||
| 167 | return AE_OK; | 172 | return AE_OK; |
| 168 | } | 173 | } |
| 169 | 174 | ||
| @@ -199,6 +204,9 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) | |||
| 199 | 204 | ||
| 200 | 205 | ||
| 201 | switch (resource->type) { | 206 | switch (resource->type) { |
| 207 | case ACPI_RESOURCE_TYPE_START_DEPENDENT: | ||
| 208 | case ACPI_RESOURCE_TYPE_END_TAG: | ||
| 209 | return AE_OK; | ||
| 202 | case ACPI_RESOURCE_TYPE_IRQ: | 210 | case ACPI_RESOURCE_TYPE_IRQ: |
| 203 | { | 211 | { |
| 204 | struct acpi_resource_irq *p = &resource->data.irq; | 212 | struct acpi_resource_irq *p = &resource->data.irq; |
| @@ -208,7 +216,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) | |||
| 208 | * particularly those those w/ _STA disabled | 216 | * particularly those those w/ _STA disabled |
| 209 | */ | 217 | */ |
| 210 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 218 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
| 211 | "Blank IRQ resource\n")); | 219 | "Blank _CRS IRQ resource\n")); |
| 212 | return AE_OK; | 220 | return AE_OK; |
| 213 | } | 221 | } |
| 214 | *irq = p->interrupts[0]; | 222 | *irq = p->interrupts[0]; |
| @@ -224,7 +232,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) | |||
| 224 | * return at least 1 IRQ | 232 | * return at least 1 IRQ |
| 225 | */ | 233 | */ |
| 226 | printk(KERN_WARNING PREFIX | 234 | printk(KERN_WARNING PREFIX |
| 227 | "Blank EXT IRQ resource\n"); | 235 | "Blank _CRS EXT IRQ resource\n"); |
| 228 | return AE_OK; | 236 | return AE_OK; |
| 229 | } | 237 | } |
| 230 | *irq = p->interrupts[0]; | 238 | *irq = p->interrupts[0]; |
| @@ -232,10 +240,11 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context) | |||
| 232 | } | 240 | } |
| 233 | break; | 241 | break; |
| 234 | default: | 242 | default: |
| 235 | printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type); | 243 | printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n", |
| 236 | case ACPI_RESOURCE_TYPE_END_TAG: | 244 | resource->type); |
| 237 | return AE_OK; | 245 | return AE_OK; |
| 238 | } | 246 | } |
| 247 | |||
| 239 | return AE_CTRL_TERMINATE; | 248 | return AE_CTRL_TERMINATE; |
| 240 | } | 249 | } |
| 241 | 250 | ||
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[] = { | |||
| 124 | 124 | ||
| 125 | static void inline | 125 | static void inline |
| 126 | acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, | 126 | acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, |
| 127 | u8 byte_width, u64 address) | 127 | u8 bit_width, u64 address) |
| 128 | { | 128 | { |
| 129 | 129 | ||
| 130 | /* | 130 | /* |
| @@ -136,7 +136,7 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, | |||
| 136 | /* All other fields are byte-wide */ | 136 | /* All other fields are byte-wide */ |
| 137 | 137 | ||
| 138 | generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; | 138 | generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; |
| 139 | generic_address->bit_width = byte_width << 3; | 139 | generic_address->bit_width = bit_width; |
| 140 | generic_address->bit_offset = 0; | 140 | generic_address->bit_offset = 0; |
| 141 | generic_address->access_width = 0; | 141 | generic_address->access_width = 0; |
| 142 | } | 142 | } |
| @@ -343,11 +343,9 @@ static void acpi_tb_convert_fadt(void) | |||
| 343 | * | 343 | * |
| 344 | * The PM event blocks are split into two register blocks, first is the | 344 | * The PM event blocks are split into two register blocks, first is the |
| 345 | * PM Status Register block, followed immediately by the PM Enable Register | 345 | * PM Status Register block, followed immediately by the PM Enable Register |
| 346 | * block. Each is of length (xpm1x_event_block.bit_width/2) | 346 | * block. Each is of length (pm1_event_length/2) |
| 347 | */ | 347 | */ |
| 348 | WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1a_event_block.bit_width)); | 348 | pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length); |
| 349 | pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT | ||
| 350 | .xpm1a_event_block.bit_width); | ||
| 351 | 349 | ||
| 352 | /* The PM1A register block is required */ | 350 | /* The PM1A register block is required */ |
| 353 | 351 | ||
| @@ -362,17 +360,14 @@ static void acpi_tb_convert_fadt(void) | |||
| 362 | /* The PM1B register block is optional, ignore if not present */ | 360 | /* The PM1B register block is optional, ignore if not present */ |
| 363 | 361 | ||
| 364 | if (acpi_gbl_FADT.xpm1b_event_block.address) { | 362 | if (acpi_gbl_FADT.xpm1b_event_block.address) { |
| 365 | WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1b_event_block.bit_width)); | ||
| 366 | pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT | ||
| 367 | .xpm1b_event_block | ||
| 368 | .bit_width); | ||
| 369 | acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable, | 363 | acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable, |
| 370 | pm1_register_length, | 364 | pm1_register_length, |
| 371 | (acpi_gbl_FADT.xpm1b_event_block. | 365 | (acpi_gbl_FADT.xpm1b_event_block. |
| 372 | address + pm1_register_length)); | 366 | address + pm1_register_length)); |
| 373 | /* Don't forget to copy space_id of the GAS */ | 367 | /* Don't forget to copy space_id of the GAS */ |
| 374 | acpi_gbl_xpm1b_enable.space_id = | 368 | acpi_gbl_xpm1b_enable.space_id = |
| 375 | acpi_gbl_FADT.xpm1b_event_block.space_id; | 369 | acpi_gbl_FADT.xpm1a_event_block.space_id; |
| 370 | |||
| 376 | } | 371 | } |
| 377 | } | 372 | } |
| 378 | 373 | ||
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 30a341337933..912703691d36 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c | |||
| @@ -769,6 +769,47 @@ static void acpi_thermal_run(unsigned long data) | |||
| 769 | acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data); | 769 | acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data); |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | static void acpi_thermal_active_off(void *data) | ||
| 773 | { | ||
| 774 | int result = 0; | ||
| 775 | struct acpi_thermal *tz = data; | ||
| 776 | int i = 0; | ||
| 777 | int j = 0; | ||
| 778 | struct acpi_thermal_active *active = NULL; | ||
| 779 | |||
| 780 | if (!tz) { | ||
| 781 | printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); | ||
| 782 | return; | ||
| 783 | } | ||
| 784 | |||
| 785 | result = acpi_thermal_get_temperature(tz); | ||
| 786 | if (result) | ||
| 787 | return; | ||
| 788 | |||
| 789 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
| 790 | active = &(tz->trips.active[i]); | ||
| 791 | if (!active || !active->flags.valid) | ||
| 792 | break; | ||
| 793 | if (tz->temperature >= active->temperature) { | ||
| 794 | /* | ||
| 795 | * If the thermal temperature is greater than the | ||
| 796 | * active threshod, unnecessary to turn off the | ||
| 797 | * the active cooling device. | ||
| 798 | */ | ||
| 799 | continue; | ||
| 800 | } | ||
| 801 | /* | ||
| 802 | * Below Threshold? | ||
| 803 | * ---------------- | ||
| 804 | * Turn OFF all cooling devices associated with this | ||
| 805 | * threshold. | ||
| 806 | */ | ||
| 807 | for (j = 0; j < active->devices.count; j++) | ||
| 808 | result = acpi_bus_set_power(active->devices.handles[j], | ||
| 809 | ACPI_STATE_D3); | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 772 | static void acpi_thermal_check(void *data) | 813 | static void acpi_thermal_check(void *data) |
| 773 | { | 814 | { |
| 774 | int result = 0; | 815 | int result = 0; |
| @@ -1624,6 +1665,8 @@ static int acpi_thermal_add(struct acpi_device *device) | |||
| 1624 | 1665 | ||
| 1625 | init_timer(&tz->timer); | 1666 | init_timer(&tz->timer); |
| 1626 | 1667 | ||
| 1668 | acpi_thermal_active_off(tz); | ||
| 1669 | |||
| 1627 | acpi_thermal_check(tz); | 1670 | acpi_thermal_check(tz); |
| 1628 | 1671 | ||
| 1629 | status = acpi_install_notify_handler(device->handle, | 1672 | status = acpi_install_notify_handler(device->handle, |
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, | |||
| 242 | { | 242 | { |
| 243 | acpi_status status = AE_OK; | 243 | acpi_status status = AE_OK; |
| 244 | 244 | ||
| 245 | if (!required_length) { | ||
| 246 | WARN_ON(1); | ||
| 247 | return AE_ERROR; | ||
| 248 | } | ||
| 245 | switch (buffer->length) { | 249 | switch (buffer->length) { |
| 246 | case ACPI_NO_BUFFER: | 250 | case ACPI_NO_BUFFER: |
| 247 | 251 | ||
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 37b9e16710d6..e8a51a1700f7 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) | |||
| 741 | 741 | ||
| 742 | max_level = acpi_video_init_brightness(device); | 742 | max_level = acpi_video_init_brightness(device); |
| 743 | 743 | ||
| 744 | if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ | 744 | if (device->cap._BCL && device->cap._BCM && max_level > 0) { |
| 745 | int result; | 745 | int result; |
| 746 | static int count = 0; | 746 | static int count = 0; |
| 747 | char *name; | 747 | char *name; |
| @@ -753,7 +753,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) | |||
| 753 | device->backlight = backlight_device_register(name, | 753 | device->backlight = backlight_device_register(name, |
| 754 | NULL, device, &acpi_backlight_ops); | 754 | NULL, device, &acpi_backlight_ops); |
| 755 | device->backlight->props.max_brightness = device->brightness->count-3; | 755 | device->backlight->props.max_brightness = device->brightness->count-3; |
| 756 | device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); | 756 | /* |
| 757 | * If there exists the _BQC object, the _BQC object will be | ||
| 758 | * called to get the current backlight brightness. Otherwise | ||
| 759 | * the brightness will be set to the maximum. | ||
| 760 | */ | ||
| 761 | if (device->cap._BQC) | ||
| 762 | device->backlight->props.brightness = | ||
| 763 | acpi_video_get_brightness(device->backlight); | ||
| 764 | else | ||
| 765 | device->backlight->props.brightness = | ||
| 766 | device->backlight->props.max_brightness; | ||
| 757 | backlight_update_status(device->backlight); | 767 | backlight_update_status(device->backlight); |
| 758 | kfree(name); | 768 | kfree(name); |
| 759 | 769 | ||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ce67d973d349..d5bc288b1b0d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -279,6 +279,8 @@ config THINKPAD_ACPI | |||
| 279 | select INPUT | 279 | select INPUT |
| 280 | select NEW_LEDS | 280 | select NEW_LEDS |
| 281 | select LEDS_CLASS | 281 | select LEDS_CLASS |
| 282 | select NET | ||
| 283 | select RFKILL | ||
| 282 | ---help--- | 284 | ---help--- |
| 283 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds | 285 | This is a driver for the IBM and Lenovo ThinkPad laptops. It adds |
| 284 | support for Fn-Fx key combinations, Bluetooth control, video | 286 | support for Fn-Fx key combinations, Bluetooth control, video |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index b5969298f3d3..d3eb7903c346 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #define TPACPI_VERSION "0.20" | 24 | #define TPACPI_VERSION "0.21" |
| 25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
| 26 | 26 | ||
| 27 | /* | 27 | /* |
| @@ -68,6 +68,7 @@ | |||
| 68 | #include <linux/hwmon-sysfs.h> | 68 | #include <linux/hwmon-sysfs.h> |
| 69 | #include <linux/input.h> | 69 | #include <linux/input.h> |
| 70 | #include <linux/leds.h> | 70 | #include <linux/leds.h> |
| 71 | #include <linux/rfkill.h> | ||
| 71 | #include <asm/uaccess.h> | 72 | #include <asm/uaccess.h> |
| 72 | 73 | ||
| 73 | #include <linux/dmi.h> | 74 | #include <linux/dmi.h> |
| @@ -144,6 +145,12 @@ enum { | |||
| 144 | 145 | ||
| 145 | #define TPACPI_MAX_ACPI_ARGS 3 | 146 | #define TPACPI_MAX_ACPI_ARGS 3 |
| 146 | 147 | ||
| 148 | /* rfkill switches */ | ||
| 149 | enum { | ||
| 150 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | ||
| 151 | TPACPI_RFK_WWAN_SW_ID, | ||
| 152 | }; | ||
| 153 | |||
| 147 | /* Debugging */ | 154 | /* Debugging */ |
| 148 | #define TPACPI_LOG TPACPI_FILE ": " | 155 | #define TPACPI_LOG TPACPI_FILE ": " |
| 149 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | 156 | #define TPACPI_ERR KERN_ERR TPACPI_LOG |
| @@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) | |||
| 905 | return 0; | 912 | return 0; |
| 906 | } | 913 | } |
| 907 | 914 | ||
| 915 | static int __init tpacpi_new_rfkill(const unsigned int id, | ||
| 916 | struct rfkill **rfk, | ||
| 917 | const enum rfkill_type rfktype, | ||
| 918 | const char *name, | ||
| 919 | int (*toggle_radio)(void *, enum rfkill_state), | ||
| 920 | int (*get_state)(void *, enum rfkill_state *)) | ||
| 921 | { | ||
| 922 | int res; | ||
| 923 | enum rfkill_state initial_state; | ||
| 924 | |||
| 925 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | ||
| 926 | if (!*rfk) { | ||
| 927 | printk(TPACPI_ERR | ||
| 928 | "failed to allocate memory for rfkill class\n"); | ||
| 929 | return -ENOMEM; | ||
| 930 | } | ||
| 931 | |||
| 932 | (*rfk)->name = name; | ||
| 933 | (*rfk)->get_state = get_state; | ||
| 934 | (*rfk)->toggle_radio = toggle_radio; | ||
| 935 | |||
| 936 | if (!get_state(NULL, &initial_state)) | ||
| 937 | (*rfk)->state = initial_state; | ||
| 938 | |||
| 939 | res = rfkill_register(*rfk); | ||
| 940 | if (res < 0) { | ||
| 941 | printk(TPACPI_ERR | ||
| 942 | "failed to register %s rfkill switch: %d\n", | ||
| 943 | name, res); | ||
| 944 | rfkill_free(*rfk); | ||
| 945 | *rfk = NULL; | ||
| 946 | return res; | ||
| 947 | } | ||
| 948 | |||
| 949 | return 0; | ||
| 950 | } | ||
| 951 | |||
| 908 | /************************************************************************* | 952 | /************************************************************************* |
| 909 | * thinkpad-acpi driver attributes | 953 | * thinkpad-acpi driver attributes |
| 910 | */ | 954 | */ |
| @@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status) | |||
| 1285 | return 0; | 1329 | return 0; |
| 1286 | } | 1330 | } |
| 1287 | 1331 | ||
| 1288 | static void tpacpi_input_send_radiosw(void) | ||
| 1289 | { | ||
| 1290 | int wlsw; | ||
| 1291 | |||
| 1292 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
| 1293 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
| 1294 | |||
| 1295 | input_report_switch(tpacpi_inputdev, | ||
| 1296 | SW_RFKILL_ALL, !!wlsw); | ||
| 1297 | input_sync(tpacpi_inputdev); | ||
| 1298 | |||
| 1299 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
| 1300 | } | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | static void tpacpi_input_send_tabletsw(void) | 1332 | static void tpacpi_input_send_tabletsw(void) |
| 1304 | { | 1333 | { |
| 1305 | int state; | 1334 | int state; |
| @@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
| 1921 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, | 1950 | &dev_attr_hotkey_wakeup_hotunplug_complete.attr, |
| 1922 | }; | 1951 | }; |
| 1923 | 1952 | ||
| 1953 | static void bluetooth_update_rfk(void); | ||
| 1954 | static void wan_update_rfk(void); | ||
| 1955 | static void tpacpi_send_radiosw_update(void) | ||
| 1956 | { | ||
| 1957 | int wlsw; | ||
| 1958 | |||
| 1959 | /* Sync these BEFORE sending any rfkill events */ | ||
| 1960 | if (tp_features.bluetooth) | ||
| 1961 | bluetooth_update_rfk(); | ||
| 1962 | if (tp_features.wan) | ||
| 1963 | wan_update_rfk(); | ||
| 1964 | |||
| 1965 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | ||
| 1966 | mutex_lock(&tpacpi_inputdev_send_mutex); | ||
| 1967 | |||
| 1968 | input_report_switch(tpacpi_inputdev, | ||
| 1969 | SW_RFKILL_ALL, !!wlsw); | ||
| 1970 | input_sync(tpacpi_inputdev); | ||
| 1971 | |||
| 1972 | mutex_unlock(&tpacpi_inputdev_send_mutex); | ||
| 1973 | } | ||
| 1974 | hotkey_radio_sw_notify_change(); | ||
| 1975 | } | ||
| 1976 | |||
| 1924 | static void hotkey_exit(void) | 1977 | static void hotkey_exit(void) |
| 1925 | { | 1978 | { |
| 1926 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL | 1979 | #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL |
| @@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
| 2167 | printk(TPACPI_INFO | 2220 | printk(TPACPI_INFO |
| 2168 | "radio switch found; radios are %s\n", | 2221 | "radio switch found; radios are %s\n", |
| 2169 | enabled(status, 0)); | 2222 | enabled(status, 0)); |
| 2223 | } | ||
| 2224 | if (tp_features.hotkey_wlsw) | ||
| 2170 | res = add_to_attr_set(hotkey_dev_attributes, | 2225 | res = add_to_attr_set(hotkey_dev_attributes, |
| 2171 | &dev_attr_hotkey_radio_sw.attr); | 2226 | &dev_attr_hotkey_radio_sw.attr); |
| 2172 | } | ||
| 2173 | 2227 | ||
| 2174 | /* For X41t, X60t, X61t Tablets... */ | 2228 | /* For X41t, X60t, X61t Tablets... */ |
| 2175 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 2229 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
| @@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
| 2287 | tpacpi_inputdev->close = &hotkey_inputdev_close; | 2341 | tpacpi_inputdev->close = &hotkey_inputdev_close; |
| 2288 | 2342 | ||
| 2289 | hotkey_poll_setup_safe(1); | 2343 | hotkey_poll_setup_safe(1); |
| 2290 | tpacpi_input_send_radiosw(); | 2344 | tpacpi_send_radiosw_update(); |
| 2291 | tpacpi_input_send_tabletsw(); | 2345 | tpacpi_input_send_tabletsw(); |
| 2292 | 2346 | ||
| 2293 | return 0; | 2347 | return 0; |
| @@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
| 2419 | case 7: | 2473 | case 7: |
| 2420 | /* 0x7000-0x7FFF: misc */ | 2474 | /* 0x7000-0x7FFF: misc */ |
| 2421 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | 2475 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { |
| 2422 | tpacpi_input_send_radiosw(); | 2476 | tpacpi_send_radiosw_update(); |
| 2423 | hotkey_radio_sw_notify_change(); | ||
| 2424 | send_acpi_ev = 0; | 2477 | send_acpi_ev = 0; |
| 2425 | break; | 2478 | break; |
| 2426 | } | 2479 | } |
| @@ -2463,8 +2516,7 @@ static void hotkey_resume(void) | |||
| 2463 | printk(TPACPI_ERR | 2516 | printk(TPACPI_ERR |
| 2464 | "error while trying to read hot key mask " | 2517 | "error while trying to read hot key mask " |
| 2465 | "from firmware\n"); | 2518 | "from firmware\n"); |
| 2466 | tpacpi_input_send_radiosw(); | 2519 | tpacpi_send_radiosw_update(); |
| 2467 | hotkey_radio_sw_notify_change(); | ||
| 2468 | hotkey_tablet_mode_notify_change(); | 2520 | hotkey_tablet_mode_notify_change(); |
| 2469 | hotkey_wakeup_reason_notify_change(); | 2521 | hotkey_wakeup_reason_notify_change(); |
| 2470 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2522 | hotkey_wakeup_hotunplug_complete_notify_change(); |
| @@ -2581,8 +2633,66 @@ enum { | |||
| 2581 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ | 2633 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ |
| 2582 | }; | 2634 | }; |
| 2583 | 2635 | ||
| 2584 | static int bluetooth_get_radiosw(void); | 2636 | static struct rfkill *tpacpi_bluetooth_rfkill; |
| 2585 | static int bluetooth_set_radiosw(int radio_on); | 2637 | |
| 2638 | static int bluetooth_get_radiosw(void) | ||
| 2639 | { | ||
| 2640 | int status; | ||
| 2641 | |||
| 2642 | if (!tp_features.bluetooth) | ||
| 2643 | return -ENODEV; | ||
| 2644 | |||
| 2645 | /* WLSW overrides bluetooth in firmware/hardware, reflect that */ | ||
| 2646 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
| 2647 | return RFKILL_STATE_HARD_BLOCKED; | ||
| 2648 | |||
| 2649 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
| 2650 | return -EIO; | ||
| 2651 | |||
| 2652 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? | ||
| 2653 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | static void bluetooth_update_rfk(void) | ||
| 2657 | { | ||
| 2658 | int status; | ||
| 2659 | |||
| 2660 | if (!tpacpi_bluetooth_rfkill) | ||
| 2661 | return; | ||
| 2662 | |||
| 2663 | status = bluetooth_get_radiosw(); | ||
| 2664 | if (status < 0) | ||
| 2665 | return; | ||
| 2666 | rfkill_force_state(tpacpi_bluetooth_rfkill, status); | ||
| 2667 | } | ||
| 2668 | |||
| 2669 | static int bluetooth_set_radiosw(int radio_on, int update_rfk) | ||
| 2670 | { | ||
| 2671 | int status; | ||
| 2672 | |||
| 2673 | if (!tp_features.bluetooth) | ||
| 2674 | return -ENODEV; | ||
| 2675 | |||
| 2676 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
| 2677 | * reason to risk weird behaviour. */ | ||
| 2678 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
| 2679 | && radio_on) | ||
| 2680 | return -EPERM; | ||
| 2681 | |||
| 2682 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
| 2683 | return -EIO; | ||
| 2684 | if (radio_on) | ||
| 2685 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | ||
| 2686 | else | ||
| 2687 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | ||
| 2688 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | ||
| 2689 | return -EIO; | ||
| 2690 | |||
| 2691 | if (update_rfk) | ||
| 2692 | bluetooth_update_rfk(); | ||
| 2693 | |||
| 2694 | return 0; | ||
| 2695 | } | ||
| 2586 | 2696 | ||
| 2587 | /* sysfs bluetooth enable ---------------------------------------------- */ | 2697 | /* sysfs bluetooth enable ---------------------------------------------- */ |
| 2588 | static ssize_t bluetooth_enable_show(struct device *dev, | 2698 | static ssize_t bluetooth_enable_show(struct device *dev, |
| @@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, | |||
| 2595 | if (status < 0) | 2705 | if (status < 0) |
| 2596 | return status; | 2706 | return status; |
| 2597 | 2707 | ||
| 2598 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2708 | return snprintf(buf, PAGE_SIZE, "%d\n", |
| 2709 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
| 2599 | } | 2710 | } |
| 2600 | 2711 | ||
| 2601 | static ssize_t bluetooth_enable_store(struct device *dev, | 2712 | static ssize_t bluetooth_enable_store(struct device *dev, |
| @@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev, | |||
| 2608 | if (parse_strtoul(buf, 1, &t)) | 2719 | if (parse_strtoul(buf, 1, &t)) |
| 2609 | return -EINVAL; | 2720 | return -EINVAL; |
| 2610 | 2721 | ||
| 2611 | res = bluetooth_set_radiosw(t); | 2722 | res = bluetooth_set_radiosw(t, 1); |
| 2612 | 2723 | ||
| 2613 | return (res) ? res : count; | 2724 | return (res) ? res : count; |
| 2614 | } | 2725 | } |
| @@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = { | |||
| 2628 | .attrs = bluetooth_attributes, | 2739 | .attrs = bluetooth_attributes, |
| 2629 | }; | 2740 | }; |
| 2630 | 2741 | ||
| 2742 | static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) | ||
| 2743 | { | ||
| 2744 | int bts = bluetooth_get_radiosw(); | ||
| 2745 | |||
| 2746 | if (bts < 0) | ||
| 2747 | return bts; | ||
| 2748 | |||
| 2749 | *state = bts; | ||
| 2750 | return 0; | ||
| 2751 | } | ||
| 2752 | |||
| 2753 | static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | ||
| 2754 | { | ||
| 2755 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
| 2756 | } | ||
| 2757 | |||
| 2758 | static void bluetooth_exit(void) | ||
| 2759 | { | ||
| 2760 | if (tpacpi_bluetooth_rfkill) | ||
| 2761 | rfkill_unregister(tpacpi_bluetooth_rfkill); | ||
| 2762 | |||
| 2763 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
| 2764 | &bluetooth_attr_group); | ||
| 2765 | } | ||
| 2766 | |||
| 2631 | static int __init bluetooth_init(struct ibm_init_struct *iibm) | 2767 | static int __init bluetooth_init(struct ibm_init_struct *iibm) |
| 2632 | { | 2768 | { |
| 2633 | int res; | 2769 | int res; |
| @@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
| 2646 | str_supported(tp_features.bluetooth), | 2782 | str_supported(tp_features.bluetooth), |
| 2647 | status); | 2783 | status); |
| 2648 | 2784 | ||
| 2649 | if (tp_features.bluetooth) { | 2785 | if (tp_features.bluetooth && |
| 2650 | if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { | 2786 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { |
| 2651 | /* no bluetooth hardware present in system */ | 2787 | /* no bluetooth hardware present in system */ |
| 2652 | tp_features.bluetooth = 0; | 2788 | tp_features.bluetooth = 0; |
| 2653 | dbg_printk(TPACPI_DBG_INIT, | 2789 | dbg_printk(TPACPI_DBG_INIT, |
| 2654 | "bluetooth hardware not installed\n"); | 2790 | "bluetooth hardware not installed\n"); |
| 2655 | } else { | ||
| 2656 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
| 2657 | &bluetooth_attr_group); | ||
| 2658 | if (res) | ||
| 2659 | return res; | ||
| 2660 | } | ||
| 2661 | } | 2791 | } |
| 2662 | 2792 | ||
| 2663 | return (tp_features.bluetooth)? 0 : 1; | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | static void bluetooth_exit(void) | ||
| 2667 | { | ||
| 2668 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
| 2669 | &bluetooth_attr_group); | ||
| 2670 | } | ||
| 2671 | |||
| 2672 | static int bluetooth_get_radiosw(void) | ||
| 2673 | { | ||
| 2674 | int status; | ||
| 2675 | |||
| 2676 | if (!tp_features.bluetooth) | 2793 | if (!tp_features.bluetooth) |
| 2677 | return -ENODEV; | 2794 | return 1; |
| 2678 | |||
| 2679 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
| 2680 | return -EIO; | ||
| 2681 | |||
| 2682 | return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); | ||
| 2683 | } | ||
| 2684 | |||
| 2685 | static int bluetooth_set_radiosw(int radio_on) | ||
| 2686 | { | ||
| 2687 | int status; | ||
| 2688 | 2795 | ||
| 2689 | if (!tp_features.bluetooth) | 2796 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
| 2690 | return -ENODEV; | 2797 | &bluetooth_attr_group); |
| 2798 | if (res) | ||
| 2799 | return res; | ||
| 2691 | 2800 | ||
| 2692 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 2801 | res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, |
| 2693 | return -EIO; | 2802 | &tpacpi_bluetooth_rfkill, |
| 2694 | if (radio_on) | 2803 | RFKILL_TYPE_BLUETOOTH, |
| 2695 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | 2804 | "tpacpi_bluetooth_sw", |
| 2696 | else | 2805 | tpacpi_bluetooth_rfk_set, |
| 2697 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | 2806 | tpacpi_bluetooth_rfk_get); |
| 2698 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 2807 | if (res) { |
| 2699 | return -EIO; | 2808 | bluetooth_exit(); |
| 2809 | return res; | ||
| 2810 | } | ||
| 2700 | 2811 | ||
| 2701 | return 0; | 2812 | return 0; |
| 2702 | } | 2813 | } |
| @@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p) | |||
| 2711 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 2822 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
| 2712 | else { | 2823 | else { |
| 2713 | len += sprintf(p + len, "status:\t\t%s\n", | 2824 | len += sprintf(p + len, "status:\t\t%s\n", |
| 2714 | (status)? "enabled" : "disabled"); | 2825 | (status == RFKILL_STATE_UNBLOCKED) ? |
| 2826 | "enabled" : "disabled"); | ||
| 2715 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 2827 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
| 2716 | } | 2828 | } |
| 2717 | 2829 | ||
| @@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf) | |||
| 2727 | 2839 | ||
| 2728 | while ((cmd = next_cmd(&buf))) { | 2840 | while ((cmd = next_cmd(&buf))) { |
| 2729 | if (strlencmp(cmd, "enable") == 0) { | 2841 | if (strlencmp(cmd, "enable") == 0) { |
| 2730 | bluetooth_set_radiosw(1); | 2842 | bluetooth_set_radiosw(1, 1); |
| 2731 | } else if (strlencmp(cmd, "disable") == 0) { | 2843 | } else if (strlencmp(cmd, "disable") == 0) { |
| 2732 | bluetooth_set_radiosw(0); | 2844 | bluetooth_set_radiosw(0, 1); |
| 2733 | } else | 2845 | } else |
| 2734 | return -EINVAL; | 2846 | return -EINVAL; |
| 2735 | } | 2847 | } |
| @@ -2755,8 +2867,66 @@ enum { | |||
| 2755 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ | 2867 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ |
| 2756 | }; | 2868 | }; |
| 2757 | 2869 | ||
| 2758 | static int wan_get_radiosw(void); | 2870 | static struct rfkill *tpacpi_wan_rfkill; |
| 2759 | static int wan_set_radiosw(int radio_on); | 2871 | |
| 2872 | static int wan_get_radiosw(void) | ||
| 2873 | { | ||
| 2874 | int status; | ||
| 2875 | |||
| 2876 | if (!tp_features.wan) | ||
| 2877 | return -ENODEV; | ||
| 2878 | |||
| 2879 | /* WLSW overrides WWAN in firmware/hardware, reflect that */ | ||
| 2880 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
| 2881 | return RFKILL_STATE_HARD_BLOCKED; | ||
| 2882 | |||
| 2883 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
| 2884 | return -EIO; | ||
| 2885 | |||
| 2886 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? | ||
| 2887 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
| 2888 | } | ||
| 2889 | |||
| 2890 | static void wan_update_rfk(void) | ||
| 2891 | { | ||
| 2892 | int status; | ||
| 2893 | |||
| 2894 | if (!tpacpi_wan_rfkill) | ||
| 2895 | return; | ||
| 2896 | |||
| 2897 | status = wan_get_radiosw(); | ||
| 2898 | if (status < 0) | ||
| 2899 | return; | ||
| 2900 | rfkill_force_state(tpacpi_wan_rfkill, status); | ||
| 2901 | } | ||
| 2902 | |||
| 2903 | static int wan_set_radiosw(int radio_on, int update_rfk) | ||
| 2904 | { | ||
| 2905 | int status; | ||
| 2906 | |||
| 2907 | if (!tp_features.wan) | ||
| 2908 | return -ENODEV; | ||
| 2909 | |||
| 2910 | /* WLSW overrides bluetooth in firmware/hardware, but there is no | ||
| 2911 | * reason to risk weird behaviour. */ | ||
| 2912 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
| 2913 | && radio_on) | ||
| 2914 | return -EPERM; | ||
| 2915 | |||
| 2916 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
| 2917 | return -EIO; | ||
| 2918 | if (radio_on) | ||
| 2919 | status |= TP_ACPI_WANCARD_RADIOSSW; | ||
| 2920 | else | ||
| 2921 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | ||
| 2922 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | ||
| 2923 | return -EIO; | ||
| 2924 | |||
| 2925 | if (update_rfk) | ||
| 2926 | wan_update_rfk(); | ||
| 2927 | |||
| 2928 | return 0; | ||
| 2929 | } | ||
| 2760 | 2930 | ||
| 2761 | /* sysfs wan enable ---------------------------------------------------- */ | 2931 | /* sysfs wan enable ---------------------------------------------------- */ |
| 2762 | static ssize_t wan_enable_show(struct device *dev, | 2932 | static ssize_t wan_enable_show(struct device *dev, |
| @@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev, | |||
| 2769 | if (status < 0) | 2939 | if (status < 0) |
| 2770 | return status; | 2940 | return status; |
| 2771 | 2941 | ||
| 2772 | return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); | 2942 | return snprintf(buf, PAGE_SIZE, "%d\n", |
| 2943 | (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); | ||
| 2773 | } | 2944 | } |
| 2774 | 2945 | ||
| 2775 | static ssize_t wan_enable_store(struct device *dev, | 2946 | static ssize_t wan_enable_store(struct device *dev, |
| @@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev, | |||
| 2782 | if (parse_strtoul(buf, 1, &t)) | 2953 | if (parse_strtoul(buf, 1, &t)) |
| 2783 | return -EINVAL; | 2954 | return -EINVAL; |
| 2784 | 2955 | ||
| 2785 | res = wan_set_radiosw(t); | 2956 | res = wan_set_radiosw(t, 1); |
| 2786 | 2957 | ||
| 2787 | return (res) ? res : count; | 2958 | return (res) ? res : count; |
| 2788 | } | 2959 | } |
| @@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = { | |||
| 2802 | .attrs = wan_attributes, | 2973 | .attrs = wan_attributes, |
| 2803 | }; | 2974 | }; |
| 2804 | 2975 | ||
| 2976 | static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) | ||
| 2977 | { | ||
| 2978 | int wans = wan_get_radiosw(); | ||
| 2979 | |||
| 2980 | if (wans < 0) | ||
| 2981 | return wans; | ||
| 2982 | |||
| 2983 | *state = wans; | ||
| 2984 | return 0; | ||
| 2985 | } | ||
| 2986 | |||
| 2987 | static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | ||
| 2988 | { | ||
| 2989 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
| 2990 | } | ||
| 2991 | |||
| 2992 | static void wan_exit(void) | ||
| 2993 | { | ||
| 2994 | if (tpacpi_wan_rfkill) | ||
| 2995 | rfkill_unregister(tpacpi_wan_rfkill); | ||
| 2996 | |||
| 2997 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
| 2998 | &wan_attr_group); | ||
| 2999 | } | ||
| 3000 | |||
| 2805 | static int __init wan_init(struct ibm_init_struct *iibm) | 3001 | static int __init wan_init(struct ibm_init_struct *iibm) |
| 2806 | { | 3002 | { |
| 2807 | int res; | 3003 | int res; |
| @@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
| 2818 | str_supported(tp_features.wan), | 3014 | str_supported(tp_features.wan), |
| 2819 | status); | 3015 | status); |
| 2820 | 3016 | ||
| 2821 | if (tp_features.wan) { | 3017 | if (tp_features.wan && |
| 2822 | if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { | 3018 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { |
| 2823 | /* no wan hardware present in system */ | 3019 | /* no wan hardware present in system */ |
| 2824 | tp_features.wan = 0; | 3020 | tp_features.wan = 0; |
| 2825 | dbg_printk(TPACPI_DBG_INIT, | 3021 | dbg_printk(TPACPI_DBG_INIT, |
| 2826 | "wan hardware not installed\n"); | 3022 | "wan hardware not installed\n"); |
| 2827 | } else { | ||
| 2828 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
| 2829 | &wan_attr_group); | ||
| 2830 | if (res) | ||
| 2831 | return res; | ||
| 2832 | } | ||
| 2833 | } | 3023 | } |
| 2834 | 3024 | ||
| 2835 | return (tp_features.wan)? 0 : 1; | ||
| 2836 | } | ||
| 2837 | |||
| 2838 | static void wan_exit(void) | ||
| 2839 | { | ||
| 2840 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, | ||
| 2841 | &wan_attr_group); | ||
| 2842 | } | ||
| 2843 | |||
| 2844 | static int wan_get_radiosw(void) | ||
| 2845 | { | ||
| 2846 | int status; | ||
| 2847 | |||
| 2848 | if (!tp_features.wan) | 3025 | if (!tp_features.wan) |
| 2849 | return -ENODEV; | 3026 | return 1; |
| 2850 | |||
| 2851 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
| 2852 | return -EIO; | ||
| 2853 | |||
| 2854 | return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); | ||
| 2855 | } | ||
| 2856 | |||
| 2857 | static int wan_set_radiosw(int radio_on) | ||
| 2858 | { | ||
| 2859 | int status; | ||
| 2860 | 3027 | ||
| 2861 | if (!tp_features.wan) | 3028 | res = sysfs_create_group(&tpacpi_pdev->dev.kobj, |
| 2862 | return -ENODEV; | 3029 | &wan_attr_group); |
| 3030 | if (res) | ||
| 3031 | return res; | ||
| 2863 | 3032 | ||
| 2864 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3033 | res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, |
| 2865 | return -EIO; | 3034 | &tpacpi_wan_rfkill, |
| 2866 | if (radio_on) | 3035 | RFKILL_TYPE_WWAN, |
| 2867 | status |= TP_ACPI_WANCARD_RADIOSSW; | 3036 | "tpacpi_wwan_sw", |
| 2868 | else | 3037 | tpacpi_wan_rfk_set, |
| 2869 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | 3038 | tpacpi_wan_rfk_get); |
| 2870 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3039 | if (res) { |
| 2871 | return -EIO; | 3040 | wan_exit(); |
| 3041 | return res; | ||
| 3042 | } | ||
| 2872 | 3043 | ||
| 2873 | return 0; | 3044 | return 0; |
| 2874 | } | 3045 | } |
| @@ -2883,7 +3054,8 @@ static int wan_read(char *p) | |||
| 2883 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3054 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
| 2884 | else { | 3055 | else { |
| 2885 | len += sprintf(p + len, "status:\t\t%s\n", | 3056 | len += sprintf(p + len, "status:\t\t%s\n", |
| 2886 | (status)? "enabled" : "disabled"); | 3057 | (status == RFKILL_STATE_UNBLOCKED) ? |
| 3058 | "enabled" : "disabled"); | ||
| 2887 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 3059 | len += sprintf(p + len, "commands:\tenable, disable\n"); |
| 2888 | } | 3060 | } |
| 2889 | 3061 | ||
| @@ -2899,9 +3071,9 @@ static int wan_write(char *buf) | |||
| 2899 | 3071 | ||
| 2900 | while ((cmd = next_cmd(&buf))) { | 3072 | while ((cmd = next_cmd(&buf))) { |
| 2901 | if (strlencmp(cmd, "enable") == 0) { | 3073 | if (strlencmp(cmd, "enable") == 0) { |
| 2902 | wan_set_radiosw(1); | 3074 | wan_set_radiosw(1, 1); |
| 2903 | } else if (strlencmp(cmd, "disable") == 0) { | 3075 | } else if (strlencmp(cmd, "disable") == 0) { |
| 2904 | wan_set_radiosw(0); | 3076 | wan_set_radiosw(0, 1); |
| 2905 | } else | 3077 | } else |
| 2906 | return -EINVAL; | 3078 | return -EINVAL; |
| 2907 | } | 3079 | } |
| @@ -6168,13 +6340,18 @@ err_out: | |||
| 6168 | 6340 | ||
| 6169 | /* Probing */ | 6341 | /* Probing */ |
| 6170 | 6342 | ||
| 6171 | static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | 6343 | /* returns 0 - probe ok, or < 0 - probe error. |
| 6344 | * Probe ok doesn't mean thinkpad found. | ||
| 6345 | * On error, kfree() cleanup on tp->* is not performed, caller must do it */ | ||
| 6346 | static int __must_check __init get_thinkpad_model_data( | ||
| 6347 | struct thinkpad_id_data *tp) | ||
| 6172 | { | 6348 | { |
| 6173 | const struct dmi_device *dev = NULL; | 6349 | const struct dmi_device *dev = NULL; |
| 6174 | char ec_fw_string[18]; | 6350 | char ec_fw_string[18]; |
| 6351 | char const *s; | ||
| 6175 | 6352 | ||
| 6176 | if (!tp) | 6353 | if (!tp) |
| 6177 | return; | 6354 | return -EINVAL; |
| 6178 | 6355 | ||
| 6179 | memset(tp, 0, sizeof(*tp)); | 6356 | memset(tp, 0, sizeof(*tp)); |
| 6180 | 6357 | ||
| @@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
| 6183 | else if (dmi_name_in_vendors("LENOVO")) | 6360 | else if (dmi_name_in_vendors("LENOVO")) |
| 6184 | tp->vendor = PCI_VENDOR_ID_LENOVO; | 6361 | tp->vendor = PCI_VENDOR_ID_LENOVO; |
| 6185 | else | 6362 | else |
| 6186 | return; | 6363 | return 0; |
| 6187 | 6364 | ||
| 6188 | tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION), | 6365 | s = dmi_get_system_info(DMI_BIOS_VERSION); |
| 6189 | GFP_KERNEL); | 6366 | tp->bios_version_str = kstrdup(s, GFP_KERNEL); |
| 6367 | if (s && !tp->bios_version_str) | ||
| 6368 | return -ENOMEM; | ||
| 6190 | if (!tp->bios_version_str) | 6369 | if (!tp->bios_version_str) |
| 6191 | return; | 6370 | return 0; |
| 6192 | tp->bios_model = tp->bios_version_str[0] | 6371 | tp->bios_model = tp->bios_version_str[0] |
| 6193 | | (tp->bios_version_str[1] << 8); | 6372 | | (tp->bios_version_str[1] << 8); |
| 6194 | 6373 | ||
| @@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
| 6207 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; | 6386 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; |
| 6208 | 6387 | ||
| 6209 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); | 6388 | tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); |
| 6389 | if (!tp->ec_version_str) | ||
| 6390 | return -ENOMEM; | ||
| 6210 | tp->ec_model = ec_fw_string[0] | 6391 | tp->ec_model = ec_fw_string[0] |
| 6211 | | (ec_fw_string[1] << 8); | 6392 | | (ec_fw_string[1] << 8); |
| 6212 | break; | 6393 | break; |
| 6213 | } | 6394 | } |
| 6214 | } | 6395 | } |
| 6215 | 6396 | ||
| 6216 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | 6397 | s = dmi_get_system_info(DMI_PRODUCT_VERSION); |
| 6217 | GFP_KERNEL); | 6398 | if (s && !strnicmp(s, "ThinkPad", 8)) { |
| 6218 | if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | 6399 | tp->model_str = kstrdup(s, GFP_KERNEL); |
| 6219 | kfree(tp->model_str); | 6400 | if (!tp->model_str) |
| 6220 | tp->model_str = NULL; | 6401 | return -ENOMEM; |
| 6221 | } | 6402 | } |
| 6222 | 6403 | ||
| 6223 | tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), | 6404 | s = dmi_get_system_info(DMI_PRODUCT_NAME); |
| 6224 | GFP_KERNEL); | 6405 | tp->nummodel_str = kstrdup(s, GFP_KERNEL); |
| 6406 | if (s && !tp->nummodel_str) | ||
| 6407 | return -ENOMEM; | ||
| 6408 | |||
| 6409 | return 0; | ||
| 6225 | } | 6410 | } |
| 6226 | 6411 | ||
| 6227 | static int __init probe_for_thinkpad(void) | 6412 | static int __init probe_for_thinkpad(void) |
| @@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void) | |||
| 6484 | 6669 | ||
| 6485 | /* Driver-level probe */ | 6670 | /* Driver-level probe */ |
| 6486 | 6671 | ||
| 6487 | get_thinkpad_model_data(&thinkpad_id); | 6672 | ret = get_thinkpad_model_data(&thinkpad_id); |
| 6673 | if (ret) { | ||
| 6674 | printk(TPACPI_ERR | ||
| 6675 | "unable to get DMI data: %d\n", ret); | ||
| 6676 | thinkpad_acpi_module_exit(); | ||
| 6677 | return ret; | ||
| 6678 | } | ||
| 6488 | ret = probe_for_thinkpad(); | 6679 | ret = probe_for_thinkpad(); |
| 6489 | if (ret) { | 6680 | if (ret) { |
| 6490 | thinkpad_acpi_module_exit(); | 6681 | thinkpad_acpi_module_exit(); |
