Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 296999
b: refs/heads/master
c: 29cd293
h: refs/heads/master
i:
  296997: efba59f
  296995: 314f3fc
  296991: f6076d0
v: v3
  • Loading branch information
Seth Forshee authored and Matthew Garrett committed Mar 22, 2012
1 parent 3d3e63d commit 52ef9f7
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 37 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3e2abc5a35d25442821e1733687b7abbc83b5072
refs/heads/master: 29cd293f9f8cd76444657622980010b9364b1de6
231 changes: 195 additions & 36 deletions trunk/drivers/platform/x86/toshiba_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/i8042.h>

#include <asm/uaccess.h>

Expand All @@ -61,6 +63,9 @@ MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");

/* Scan code for Fn key on TOS1900 models */
#define TOS1900_FN_SCAN 0x6e

/* Toshiba ACPI method paths */
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"

Expand Down Expand Up @@ -95,6 +100,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS 0x0056

/* field definitions */
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
Expand All @@ -111,6 +118,7 @@ struct toshiba_acpi_dev {
const char *method_hci;
struct rfkill *bt_rfk;
struct input_dev *hotkey_dev;
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;

Expand All @@ -122,10 +130,14 @@ struct toshiba_acpi_dev {
unsigned int video_supported:1;
unsigned int fan_supported:1;
unsigned int system_event_supported:1;
unsigned int ntfy_supported:1;
unsigned int info_supported:1;

struct mutex mutex;
};

static struct toshiba_acpi_dev *toshiba_acpi;

static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6208", 0},
Expand Down Expand Up @@ -847,10 +859,78 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};

static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
if (str & 0x20)
return false;

if (unlikely(data == 0xe0))
return false;

if ((data & 0x7f) == TOS1900_FN_SCAN) {
schedule_work(&toshiba_acpi->hotkey_work);
return true;
}

return false;
}

static void toshiba_acpi_hotkey_work(struct work_struct *work)
{
acpi_handle ec_handle = ec_get_handle();
acpi_status status;

if (!ec_handle)
return;

status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
if (ACPI_FAILURE(status))
pr_err("ACPI NTFY method execution failed\n");
}

/*
* Returns hotkey scancode, or < 0 on failure.
*/
static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
{
struct acpi_buffer buf;
union acpi_object out_obj;
acpi_status status;

buf.pointer = &out_obj;
buf.length = sizeof(out_obj);

status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO",
NULL, &buf);
if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) {
pr_err("ACPI INFO method execution failed\n");
return -EIO;
}

return out_obj.integer.value;
}

static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
int scancode)
{
if (scancode == 0x100)
return;

/* act on key press; ignore key release */
if (scancode & 0x80)
return;

if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
pr_info("Unknown key %x\n", scancode);
}

static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
acpi_status status;
acpi_handle ec_handle, handle;
int error;
u32 hci_result;

dev->hotkey_dev = input_allocate_device();
if (!dev->hotkey_dev) {
Expand All @@ -866,21 +946,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
if (error)
goto err_free_dev;

/*
* For some machines the SCI responsible for providing hotkey
* notification doesn't fire. We can trigger the notification
* whenever the Fn key is pressed using the NTFY method, if
* supported, so if it's present set up an i8042 key filter
* for this purpose.
*/
status = AE_ERROR;
ec_handle = ec_get_handle();
if (ec_handle)
status = acpi_get_handle(ec_handle, "NTFY", &handle);

if (ACPI_SUCCESS(status)) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);

error = i8042_install_filter(toshiba_acpi_i8042_filter);
if (error) {
pr_err("Error installing key filter\n");
goto err_free_keymap;
}

dev->ntfy_supported = 1;
}

/*
* Determine hotkey query interface. Prefer using the INFO
* method when it is available.
*/
status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle);
if (ACPI_SUCCESS(status)) {
dev->info_supported = 1;
} else {
hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
if (hci_result == HCI_SUCCESS)
dev->system_event_supported = 1;
}

if (!dev->info_supported && !dev->system_event_supported) {
pr_warn("No hotkey query interface found\n");
goto err_remove_filter;
}

status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
pr_info("Unable to enable hotkeys\n");
error = -ENODEV;
goto err_free_keymap;
goto err_remove_filter;
}

error = input_register_device(dev->hotkey_dev);
if (error) {
pr_info("Unable to register input device\n");
goto err_free_keymap;
goto err_remove_filter;
}

hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
return 0;

err_remove_filter:
if (dev->ntfy_supported)
i8042_remove_filter(toshiba_acpi_i8042_filter);
err_free_keymap:
sparse_keymap_free(dev->hotkey_dev);
err_free_dev:
Expand All @@ -895,6 +1021,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)

remove_toshiba_proc_entries(dev);

if (dev->ntfy_supported) {
i8042_remove_filter(toshiba_acpi_i8042_filter);
cancel_work_sync(&dev->hotkey_work);
}

if (dev->hotkey_dev) {
input_unregister_device(dev->hotkey_dev);
sparse_keymap_free(dev->hotkey_dev);
Expand All @@ -911,6 +1042,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);

if (toshiba_acpi)
toshiba_acpi = NULL;

kfree(dev);

return 0;
Expand All @@ -936,12 +1070,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev;
const char *hci_method;
u32 hci_result;
u32 dummy;
bool bt_present;
int ret = 0;
struct backlight_properties props;

if (toshiba_acpi)
return -EBUSY;

pr_info("Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);

Expand All @@ -963,11 +1099,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)

mutex_init(&dev->mutex);

/* enable event fifo */
hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
if (hci_result == HCI_SUCCESS)
dev->system_event_supported = 1;

props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
dev->backlight_dev = backlight_device_register("toshiba",
Expand Down Expand Up @@ -1024,6 +1155,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)

create_toshiba_proc_entries(dev);

toshiba_acpi = dev;

return 0;

error:
Expand All @@ -1036,40 +1169,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 hci_result, value;
int retries = 3;
int scancode;

if (!dev->system_event_supported || event != 0x80)
if (event != 0x80)
return;

do {
hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
switch (hci_result) {
case HCI_SUCCESS:
if (value == 0x100)
continue;
/* act on key press; ignore key release */
if (value & 0x80)
continue;

if (!sparse_keymap_report_event(dev->hotkey_dev,
value, 1, true)) {
pr_info("Unknown key %x\n",
value);
if (dev->info_supported) {
scancode = toshiba_acpi_query_hotkey(dev);
if (scancode < 0)
pr_err("Failed to query hotkey event\n");
else if (scancode != 0)
toshiba_acpi_report_hotkey(dev, scancode);
} else if (dev->system_event_supported) {
do {
hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
switch (hci_result) {
case HCI_SUCCESS:
toshiba_acpi_report_hotkey(dev, (int)value);
break;
case HCI_NOT_SUPPORTED:
/*
* This is a workaround for an unresolved
* issue on some machines where system events
* sporadically become disabled.
*/
hci_write1(dev, HCI_SYSTEM_EVENT, 1,
&hci_result);
pr_notice("Re-enabled hotkeys\n");
/* fall through */
default:
retries--;
break;
}
break;
case HCI_NOT_SUPPORTED:
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
* become disabled. */
hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
pr_notice("Re-enabled hotkeys\n");
/* fall through */
default:
retries--;
break;
}
} while (retries && hci_result != HCI_EMPTY);
} while (retries && hci_result != HCI_EMPTY);
}
}

static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
pm_message_t state)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 result;

if (dev->hotkey_dev)
hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);

return 0;
}

static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 result;

if (dev->hotkey_dev)
hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);

return 0;
}

static struct acpi_driver toshiba_acpi_driver = {
.name = "Toshiba ACPI driver",
Expand All @@ -1080,6 +1237,8 @@ static struct acpi_driver toshiba_acpi_driver = {
.add = toshiba_acpi_add,
.remove = toshiba_acpi_remove,
.notify = toshiba_acpi_notify,
.suspend = toshiba_acpi_suspend,
.resume = toshiba_acpi_resume,
},
};

Expand Down

0 comments on commit 52ef9f7

Please sign in to comment.