提交 27a3353a 编写于 作者: L Linus Torvalds

Merge branch 'for_linus' of...

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (32 commits)
  Move N014, N051 and CR620 dmi information to load scm dmi table
  drivers/platform/x86/eeepc-wmi.c: fix build warning
  X86 platfrom wmi: Add debug facility to dump WMI data in a readable way
  X86 platform wmi: Also log GUID string when an event happens and debug is set
  X86 platform wmi: Introduce debug param to log all WMI events
  Clean up all objects used by scm model when driver initial fail or exit
  msi-laptop: fix up some coding style issues found by checkpatch
  msi-laptop: Add i8042 filter to sync sw state with BIOS when function key pressed
  msi-laptop: Set rfkill init state when msi-laptop intiial
  msi-laptop: Add MSI CR620 notebook dmi information to scm models table
  msi-laptop: Add N014 N051 dmi information to scm models table
  drivers/platform/x86: Use kmemdup
  drivers/platform/x86: Use kzalloc
  drivers/platform/x86: Clarify the MRST IPC driver description slightly
  eeepc-wmi: depends on BACKLIGHT_CLASS_DEVICE
  IPC driver for Intel Mobile Internet Device (MID) platforms
  classmate-laptop: Add RFKILL support.
  thinkpad-acpi: document backlight level writeback at driver init
  thinkpad-acpi: clean up ACPI handles handling
  thinkpad-acpi: don't depend on led_path for led firmware type (v2)
  ...
......@@ -292,13 +292,13 @@ sysfs notes:
Warning: when in NVRAM mode, the volume up/down/mute
keys are synthesized according to changes in the mixer,
so you have to use volume up or volume down to unmute,
as per the ThinkPad volume mixer user interface. When
in ACPI event mode, volume up/down/mute are reported as
separate events, but this behaviour may be corrected in
future releases of this driver, in which case the
ThinkPad volume mixer user interface semantics will be
enforced.
which uses a single volume up or volume down hotkey
press to unmute, as per the ThinkPad volume mixer user
interface. When in ACPI event mode, volume up/down/mute
events are reported by the firmware and can behave
differently (and that behaviour changes with firmware
version -- not just with firmware models -- as well as
OSI(Linux) state).
hotkey_poll_freq:
frequency in Hz for hot key polling. It must be between
......@@ -309,7 +309,7 @@ sysfs notes:
will cause hot key presses that require NVRAM polling
to never be reported.
Setting hotkey_poll_freq too low will cause repeated
Setting hotkey_poll_freq too low may cause repeated
pressings of the same hot key to be misreported as a
single key press, or to not even be detected at all.
The recommended polling frequency is 10Hz.
......@@ -397,6 +397,7 @@ ACPI Scan
event code Key Notes
0x1001 0x00 FN+F1 -
0x1002 0x01 FN+F2 IBM: battery (rare)
Lenovo: Screen lock
......@@ -404,7 +405,8 @@ event code Key Notes
this hot key, even with hot keys
disabled or with Fn+F3 masked
off
IBM: screen lock
IBM: screen lock, often turns
off the ThinkLight as side-effect
Lenovo: battery
0x1004 0x03 FN+F4 Sleep button (ACPI sleep button
......@@ -433,7 +435,8 @@ event code Key Notes
Do you feel lucky today?
0x1008 0x07 FN+F8 IBM: toggle screen expand
Lenovo: configure UltraNav
Lenovo: configure UltraNav,
or toggle screen expand
0x1009 0x08 FN+F9 -
.. .. ..
......@@ -444,7 +447,7 @@ event code Key Notes
either through the ACPI event,
or through a hotkey event.
The firmware may refuse to
generate further FN+F4 key
generate further FN+F12 key
press events until a S3 or S4
ACPI sleep cycle is performed,
or some time passes.
......@@ -512,15 +515,19 @@ events for switches:
SW_RFKILL_ALL T60 and later hardware rfkill rocker switch
SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A
Non hot-key ACPI HKEY event map:
Non hotkey ACPI HKEY event map:
-------------------------------
Events that are not propagated by the driver, except for legacy
compatibility purposes when hotkey_report_mode is set to 1:
0x5001 Lid closed
0x5002 Lid opened
0x5009 Tablet swivel: switched to tablet mode
0x500A Tablet swivel: switched to normal mode
0x7000 Radio Switch may have changed state
The above events are not propagated by the driver, except for legacy
compatibility purposes when hotkey_report_mode is set to 1.
Events that are never propagated by the driver:
0x2304 System is waking up from suspend to undock
0x2305 System is waking up from suspend to eject bay
......@@ -528,14 +535,39 @@ compatibility purposes when hotkey_report_mode is set to 1.
0x2405 System is waking up from hibernation to eject bay
0x5010 Brightness level changed/control event
The above events are never propagated by the driver.
Events that are propagated by the driver to userspace:
0x2313 ALARM: System is waking up from suspend because
the battery is nearly empty
0x2413 ALARM: System is waking up from hibernation because
the battery is nearly empty
0x3003 Bay ejection (see 0x2x05) complete, can sleep again
0x3006 Bay hotplug request (hint to power up SATA link when
the optical drive tray is ejected)
0x4003 Undocked (see 0x2x04), can sleep again
0x500B Tablet pen inserted into its storage bay
0x500C Tablet pen removed from its storage bay
The above events are propagated by the driver.
0x6011 ALARM: battery is too hot
0x6012 ALARM: battery is extremely hot
0x6021 ALARM: a sensor is too hot
0x6022 ALARM: a sensor is extremely hot
0x6030 System thermal table changed
Battery nearly empty alarms are a last resort attempt to get the
operating system to hibernate or shutdown cleanly (0x2313), or shutdown
cleanly (0x2413) before power is lost. They must be acted upon, as the
wake up caused by the firmware will have negated most safety nets...
When any of the "too hot" alarms happen, according to Lenovo the user
should suspend or hibernate the laptop (and in the case of battery
alarms, unplug the AC adapter) to let it cool down. These alarms do
signal that something is wrong, they should never happen on normal
operating conditions.
The "extremely hot" alarms are emergencies. According to Lenovo, the
operating system is to force either an immediate suspend or hibernate
cycle, or a system shutdown. Obviously, something is very wrong if this
happens.
Compatibility notes:
......
#ifndef _ASM_X86_INTEL_SCU_IPC_H_
#define _ASM_X86_INTEL_SCU_IPC_H_
/* Read single register */
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
/* Read two sequential registers */
int intel_scu_ipc_ioread16(u16 addr, u16 *data);
/* Read four sequential registers */
int intel_scu_ipc_ioread32(u16 addr, u32 *data);
/* Read a vector */
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
/* Write single register */
int intel_scu_ipc_iowrite8(u16 addr, u8 data);
/* Write two sequential registers */
int intel_scu_ipc_iowrite16(u16 addr, u16 data);
/* Write four sequential registers */
int intel_scu_ipc_iowrite32(u16 addr, u32 data);
/* Write a vector */
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
/* Update single register based on the mask */
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
/*
* Indirect register read
* Can be used when SCCB(System Controller Configuration Block) register
* HRIM(Honor Restricted IPC Messages) is set (bit 23)
*/
int intel_scu_ipc_register_read(u32 addr, u32 *data);
/*
* Indirect register write
* Can be used when SCCB(System Controller Configuration Block) register
* HRIM(Honor Restricted IPC Messages) is set (bit 23)
*/
int intel_scu_ipc_register_write(u32 addr, u32 data);
/* Issue commands to the SCU with or without data */
int intel_scu_ipc_simple_command(int cmd, int sub);
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
u32 *out, int outlen);
/* I2C control api */
int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
/* Update FW version */
int intel_scu_ipc_fw_update(u8 *buffer, u32 length);
#endif
......@@ -390,6 +390,7 @@ config EEEPC_WMI
depends on ACPI_WMI
depends on INPUT
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
......@@ -527,4 +528,13 @@ config ACPI_CMPC
keys as input device, backlight device, tablet and accelerometer
devices.
config INTEL_SCU_IPC
bool "Intel SCU IPC Support"
depends on X86_MRST
default y
---help---
IPC is used to bridge the communications between kernel and SCU on
some embedded Intel x86 platforms. This is not needed for PC-type
machines.
endif # X86_PLATFORM_DEVICES
......@@ -25,3 +25,4 @@ obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
......@@ -24,6 +24,7 @@
#include <acpi/acpi_drivers.h>
#include <linux/backlight.h>
#include <linux/input.h>
#include <linux/rfkill.h>
MODULE_LICENSE("GPL");
......@@ -37,7 +38,7 @@ struct cmpc_accel {
#define CMPC_ACCEL_HID "ACCE0000"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_BL_HID "IPML200"
#define CMPC_IPML_HID "IPML200"
#define CMPC_KEYS_HID "FnBT0000"
/*
......@@ -461,43 +462,168 @@ static const struct backlight_ops cmpc_bl_ops = {
.update_status = cmpc_bl_update_status
};
static int cmpc_bl_add(struct acpi_device *acpi)
/*
* RFKILL code.
*/
static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
unsigned long long *value)
{
struct backlight_properties props;
union acpi_object param;
struct acpi_object_list input;
unsigned long long output;
acpi_status status;
param.type = ACPI_TYPE_INTEGER;
param.integer.value = 0xC1;
input.count = 1;
input.pointer = &param;
status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
if (ACPI_SUCCESS(status))
*value = output;
return status;
}
static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
unsigned long long value)
{
union acpi_object param[2];
struct acpi_object_list input;
acpi_status status;
unsigned long long output;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0xC1;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = value;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
return status;
}
static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
{
acpi_status status;
acpi_handle handle;
unsigned long long state;
bool blocked;
handle = data;
status = cmpc_get_rfkill_wlan(handle, &state);
if (ACPI_SUCCESS(status)) {
blocked = state & 1 ? false : true;
rfkill_set_sw_state(rfkill, blocked);
}
}
static int cmpc_rfkill_block(void *data, bool blocked)
{
acpi_status status;
acpi_handle handle;
unsigned long long state;
handle = data;
status = cmpc_get_rfkill_wlan(handle, &state);
if (ACPI_FAILURE(status))
return -ENODEV;
if (blocked)
state &= ~1;
else
state |= 1;
status = cmpc_set_rfkill_wlan(handle, state);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
}
static const struct rfkill_ops cmpc_rfkill_ops = {
.query = cmpc_rfkill_query,
.set_block = cmpc_rfkill_block,
};
/*
* Common backlight and rfkill code.
*/
struct ipml200_dev {
struct backlight_device *bd;
struct rfkill *rf;
};
static int cmpc_ipml_add(struct acpi_device *acpi)
{
int retval;
struct ipml200_dev *ipml;
struct backlight_properties props;
ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
if (ipml == NULL)
return -ENOMEM;
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 7;
bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle,
&cmpc_bl_ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
dev_set_drvdata(&acpi->dev, bd);
ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
acpi->handle, &cmpc_bl_ops,
&props);
if (IS_ERR(ipml->bd)) {
retval = PTR_ERR(ipml->bd);
goto out_bd;
}
ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
&cmpc_rfkill_ops, acpi->handle);
/* rfkill_alloc may fail if RFKILL is disabled. We should still work
* anyway. */
if (!IS_ERR(ipml->rf)) {
retval = rfkill_register(ipml->rf);
if (retval) {
rfkill_destroy(ipml->rf);
ipml->rf = NULL;
}
} else {
ipml->rf = NULL;
}
dev_set_drvdata(&acpi->dev, ipml);
return 0;
out_bd:
kfree(ipml);
return retval;
}
static int cmpc_bl_remove(struct acpi_device *acpi, int type)
static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
{
struct backlight_device *bd;
struct ipml200_dev *ipml;
ipml = dev_get_drvdata(&acpi->dev);
backlight_device_unregister(ipml->bd);
if (ipml->rf) {
rfkill_unregister(ipml->rf);
rfkill_destroy(ipml->rf);
}
kfree(ipml);
bd = dev_get_drvdata(&acpi->dev);
backlight_device_unregister(bd);
return 0;
}
static const struct acpi_device_id cmpc_bl_device_ids[] = {
{CMPC_BL_HID, 0},
static const struct acpi_device_id cmpc_ipml_device_ids[] = {
{CMPC_IPML_HID, 0},
{"", 0}
};
static struct acpi_driver cmpc_bl_acpi_driver = {
static struct acpi_driver cmpc_ipml_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc",
.class = "cmpc",
.ids = cmpc_bl_device_ids,
.ids = cmpc_ipml_device_ids,
.ops = {
.add = cmpc_bl_add,
.remove = cmpc_bl_remove
.add = cmpc_ipml_add,
.remove = cmpc_ipml_remove
}
};
......@@ -580,7 +706,7 @@ static int cmpc_init(void)
if (r)
goto failed_keys;
r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
if (r)
goto failed_bl;
......@@ -598,7 +724,7 @@ static int cmpc_init(void)
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
failed_tablet:
acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
failed_bl:
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
......@@ -611,7 +737,7 @@ static void cmpc_exit(void)
{
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
}
......@@ -621,7 +747,7 @@ module_exit(cmpc_exit);
static const struct acpi_device_id cmpc_device_ids[] = {
{CMPC_ACCEL_HID, 0},
{CMPC_TABLET_HID, 0},
{CMPC_BL_HID, 0},
{CMPC_IPML_HID, 0},
{CMPC_KEYS_HID, 0},
{"", 0}
};
......
......@@ -206,7 +206,7 @@ static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
{
struct backlight_device *bd = eeepc->backlight_device;
int old = bd->props.brightness;
int new;
int new = old;
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
new = code - NOTIFY_BRNUP_MIN + 1;
......
......@@ -1090,10 +1090,9 @@ static int __init fujitsu_init(void)
if (acpi_disabled)
return -ENODEV;
fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
fujitsu = kzalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
if (!fujitsu)
return -ENOMEM;
memset(fujitsu, 0, sizeof(struct fujitsu_t));
fujitsu->keycode1 = KEY_PROG1;
fujitsu->keycode2 = KEY_PROG2;
fujitsu->keycode3 = KEY_PROG3;
......@@ -1150,12 +1149,11 @@ static int __init fujitsu_init(void)
/* Register hotkey driver */
fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
fujitsu_hotkey = kzalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
if (!fujitsu_hotkey) {
ret = -ENOMEM;
goto fail_hotkey;
}
memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
if (result < 0) {
......
此差异已折叠。
......@@ -59,6 +59,7 @@
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/i8042.h>
#define MSI_DRIVER_VERSION "0.5"
......@@ -118,7 +119,8 @@ static int set_lcd_level(int level)
buf[0] = 0x80;
buf[1] = (u8) (level*31);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf),
NULL, 0, 1);
}
static int get_lcd_level(void)
......@@ -126,7 +128,8 @@ static int get_lcd_level(void)
u8 wdata = 0, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
&rdata, 1, 1);
if (result < 0)
return result;
......@@ -138,7 +141,8 @@ static int get_auto_brightness(void)
u8 wdata = 4, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1,
&rdata, 1, 1);
if (result < 0)
return result;
......@@ -152,14 +156,16 @@ static int set_auto_brightness(int enable)
wdata[0] = 4;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1,
&rdata, 1, 1);
if (result < 0)
return result;
wdata[0] = 0x84;
wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2,
NULL, 0, 1);
}
static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
......@@ -254,7 +260,7 @@ static int bl_update_status(struct backlight_device *b)
return set_lcd_level(b->props.brightness);
}
static struct backlight_ops msibl_ops = {
static const struct backlight_ops msibl_ops = {
.get_brightness = bl_get_brightness,
.update_status = bl_update_status,
};
......@@ -353,7 +359,8 @@ static ssize_t store_lcd_level(struct device *dev,
int level, ret;
if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
if (sscanf(buf, "%i", &level) != 1 ||
(level < 0 || level >= MSI_LCD_LEVEL_MAX))
return -EINVAL;
ret = set_lcd_level(level);
......@@ -393,7 +400,8 @@ static ssize_t store_auto_brightness(struct device *dev,
}
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
store_auto_brightness);
static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
......@@ -424,8 +432,9 @@ static struct platform_device *msipf_device;
static int dmi_check_cb(const struct dmi_system_id *id)
{
printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
return 0;
printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
id->ident);
return 0;
}
static struct dmi_system_id __initdata msi_dmi_table[] = {
......@@ -435,7 +444,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INT'L CO.,LTD")
},
.callback = dmi_check_cb
},
......@@ -465,7 +475,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INT'L CO.,LTD")
},
.callback = dmi_check_cb
},
......@@ -484,6 +495,35 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
},
.callback = dmi_check_cb
},
{
.ident = "MSI N051",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"MICRO-STAR INTERNATIONAL CO., LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"),
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INTERNATIONAL CO., LTD")
},
.callback = dmi_check_cb
},
{
.ident = "MSI N014",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"MICRO-STAR INTERNATIONAL CO., LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
},
.callback = dmi_check_cb
},
{
.ident = "MSI CR620",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
},
.callback = dmi_check_cb
},
{ }
};
......@@ -552,11 +592,71 @@ static void rfkill_cleanup(void)
}
}
static void msi_update_rfkill(struct work_struct *ignored)
{
get_wireless_state_ec_standard();
if (rfk_wlan)
rfkill_set_sw_state(rfk_wlan, !wlan_s);
if (rfk_bluetooth)
rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
if (rfk_threeg)
rfkill_set_sw_state(rfk_threeg, !threeg_s);
}
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
static bool extended;
if (str & 0x20)
return false;
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/
if (unlikely(data == 0xe0)) {
extended = true;
return false;
} else if (unlikely(extended)) {
switch (data) {
case 0x54:
case 0x62:
case 0x76:
schedule_delayed_work(&msi_rfkill_work,
round_jiffies_relative(0.5 * HZ));
break;
}
extended = false;
}
return false;
}
static void msi_init_rfkill(struct work_struct *ignored)
{
if (rfk_wlan) {
rfkill_set_sw_state(rfk_wlan, !wlan_s);
rfkill_wlan_set(NULL, !wlan_s);
}
if (rfk_bluetooth) {
rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
rfkill_bluetooth_set(NULL, !bluetooth_s);
}
if (rfk_threeg) {
rfkill_set_sw_state(rfk_threeg, !threeg_s);
rfkill_threeg_set(NULL, !threeg_s);
}
}
static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill);
static int rfkill_init(struct platform_device *sdev)
{
/* add rfkill */
int retval;
/* keep the hardware wireless state */
get_wireless_state_ec_standard();
rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev,
RFKILL_TYPE_BLUETOOTH,
&rfkill_bluetooth_ops, NULL);
......@@ -590,6 +690,10 @@ static int rfkill_init(struct platform_device *sdev)
goto err_threeg;
}
/* schedule to run rfkill state initial */
schedule_delayed_work(&msi_rfkill_init,
round_jiffies_relative(1 * HZ));
return 0;
err_threeg:
......@@ -653,9 +757,24 @@ static int load_scm_model_init(struct platform_device *sdev)
/* initial rfkill */
result = rfkill_init(sdev);
if (result < 0)
return result;
goto fail_rfkill;
result = i8042_install_filter(msi_laptop_i8042_filter);
if (result) {
printk(KERN_ERR
"msi-laptop: Unable to install key filter\n");
goto fail_filter;
}
return 0;
fail_filter:
rfkill_cleanup();
fail_rfkill:
return result;
}
static int __init msi_init(void)
......@@ -714,7 +833,8 @@ static int __init msi_init(void)
goto fail_platform_device1;
}
ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
ret = sysfs_create_group(&msipf_device->dev.kobj,
&msipf_attribute_group);
if (ret)
goto fail_platform_device2;
......@@ -739,6 +859,11 @@ static int __init msi_init(void)
fail_platform_device2:
if (load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter);
cancel_delayed_work_sync(&msi_rfkill_work);
rfkill_cleanup();
}
platform_device_del(msipf_device);
fail_platform_device1:
......@@ -758,6 +883,11 @@ static int __init msi_init(void)
static void __exit msi_cleanup(void)
{
if (load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter);
cancel_delayed_work_sync(&msi_rfkill_work);
rfkill_cleanup();
}
sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
if (!old_ec_model && threeg_exists)
......@@ -766,8 +896,6 @@ static void __exit msi_cleanup(void)
platform_driver_unregister(&msipf_driver);
backlight_device_unregister(msibl_device);
rfkill_cleanup();
/* Enable automatic brightness control again */
if (auto_brightness != 2)
set_auto_brightness(1);
......@@ -788,3 +916,6 @@ MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-105
MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
......@@ -81,6 +81,16 @@ static struct wmi_block wmi_blocks;
#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
static int debug_event;
module_param(debug_event, bool, 0444);
MODULE_PARM_DESC(debug_event,
"Log WMI Events [0/1]");
static int debug_dump_wdg;
module_param(debug_dump_wdg, bool, 0444);
MODULE_PARM_DESC(debug_dump_wdg,
"Dump available WMI interfaces [0/1]");
static int acpi_wmi_remove(struct acpi_device *device, int type);
static int acpi_wmi_add(struct acpi_device *device);
static void acpi_wmi_notify(struct acpi_device *device, u32 event);
......@@ -477,6 +487,64 @@ const struct acpi_buffer *in)
}
EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(struct guid_block *g)
{
char guid_string[37];
wmi_gtoa(g->guid, guid_string);
printk(KERN_INFO PREFIX "%s:\n", guid_string);
printk(KERN_INFO PREFIX "\tobject_id: %c%c\n",
g->object_id[0], g->object_id[1]);
printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id);
printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved);
printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count);
printk(KERN_INFO PREFIX "\tflags: %#x", g->flags);
if (g->flags) {
printk(" ");
if (g->flags & ACPI_WMI_EXPENSIVE)
printk("ACPI_WMI_EXPENSIVE ");
if (g->flags & ACPI_WMI_METHOD)
printk("ACPI_WMI_METHOD ");
if (g->flags & ACPI_WMI_STRING)
printk("ACPI_WMI_STRING ");
if (g->flags & ACPI_WMI_EVENT)
printk("ACPI_WMI_EVENT ");
}
printk("\n");
}
static void wmi_notify_debug(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
wmi_get_event_data(value, &response);
obj = (union acpi_object *)response.pointer;
if (!obj)
return;
printk(KERN_INFO PREFIX "DEBUG Event ");
switch(obj->type) {
case ACPI_TYPE_BUFFER:
printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
break;
case ACPI_TYPE_STRING:
printk("STRING_TYPE - %s\n", obj->string.pointer);
break;
case ACPI_TYPE_INTEGER:
printk("INTEGER_TYPE - %llu\n", obj->integer.value);
break;
case ACPI_TYPE_PACKAGE:
printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
break;
default:
printk("object type 0x%X\n", obj->type);
}
}
/**
* wmi_install_notify_handler - Register handler for WMI events
* @handler: Function to handle notifications
......@@ -496,7 +564,7 @@ wmi_notify_handler handler, void *data)
if (!find_guid(guid, &block))
return AE_NOT_EXIST;
if (block->handler)
if (block->handler && block->handler != wmi_notify_debug)
return AE_ALREADY_ACQUIRED;
block->handler = handler;
......@@ -516,7 +584,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
acpi_status wmi_remove_notify_handler(const char *guid)
{
struct wmi_block *block;
acpi_status status;
acpi_status status = AE_OK;
if (!guid)
return AE_BAD_PARAMETER;
......@@ -524,14 +592,16 @@ acpi_status wmi_remove_notify_handler(const char *guid)
if (!find_guid(guid, &block))
return AE_NOT_EXIST;
if (!block->handler)
if (!block->handler || block->handler == wmi_notify_debug)
return AE_NULL_ENTRY;
status = wmi_method_enable(block, 0);
block->handler = NULL;
block->handler_data = NULL;
if (debug_event) {
block->handler = wmi_notify_debug;
} else {
status = wmi_method_enable(block, 0);
block->handler = NULL;
block->handler_data = NULL;
}
return status;
}
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
......@@ -756,12 +826,10 @@ static __init acpi_status parse_wdg(acpi_handle handle)
total = obj->buffer.length / sizeof(struct guid_block);
gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
if (!gblock)
return AE_NO_MEMORY;
memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
for (i = 0; i < total; i++) {
/*
Some WMI devices, like those for nVidia hooks, have a
......@@ -776,12 +844,19 @@ static __init acpi_status parse_wdg(acpi_handle handle)
guid_string);
continue;
}
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock)
return AE_NO_MEMORY;
wblock->gblock = gblock[i];
wblock->handle = handle;
if (debug_event) {
wblock->handler = wmi_notify_debug;
status = wmi_method_enable(wblock, 1);
}
list_add_tail(&wblock->list, &wmi_blocks.list);
}
......@@ -840,6 +915,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
struct guid_block *block;
struct wmi_block *wblock;
struct list_head *p;
char guid_string[37];
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
......@@ -849,6 +925,11 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
(block->notify_id == event)) {
if (wblock->handler)
wblock->handler(event, wblock->handler_data);
if (debug_event) {
wmi_gtoa(wblock->gblock.guid, guid_string);
printk(KERN_INFO PREFIX "DEBUG Event GUID:"
" %s\n", guid_string);
}
acpi_bus_generate_netlink_event(
device->pnp.device_class, dev_name(&device->dev),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册