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: (44 commits)
  eeepc-wmi: Add cpufv sysfs interface
  eeepc-wmi: add additional hotkeys
  panasonic-laptop: Simplify calls to acpi_pcc_retrieve_biosdata
  panasonic-laptop: Handle errors properly if they happen
  intel_pmic_gpio: fix off-by-one value range checking
  IBM Real-Time "SMI Free" mode driver -v7
  Add OLPC XO-1 rfkill driver
  Move hdaps driver to platform/x86
  ideapad-laptop: Fix Makefile
  intel_pmic_gpio: swap the bits and mask args for intel_scu_ipc_update_register
  ideapad: Add param: no_bt_rfkill
  ideapad: Change the driver name to ideapad-laptop
  ideapad: rewrite the sw rfkill set
  ideapad: rewrite the hw rfkill notify
  ideapad: use EC command to control camera
  ideapad: use return value of _CFG to tell if device exist or not
  ideapad: make sure we bind on the correct device
  ideapad: check VPC bit before sync rfkill hw status
  ideapad: add ACPI helpers
  dell-laptop: Add debugfs support
  ...
This commit is contained in:
Linus Torvalds 2010-10-25 08:28:13 -07:00
commit fbaab1dc19
24 changed files with 1385 additions and 1050 deletions

View file

@ -0,0 +1,22 @@
What: state
Date: Sep 2010
KernelVersion: 2.6.37
Contact: Vernon Mauery <vernux@us.ibm.com>
Description: The state file allows a means by which to change in and
out of Premium Real-Time Mode (PRTM), as well as the
ability to query the current state.
0 => PRTM off
1 => PRTM enabled
Users: The ibm-prtm userspace daemon uses this interface.
What: version
Date: Sep 2010
KernelVersion: 2.6.37
Contact: Vernon Mauery <vernux@us.ibm.com>
Description: The version file provides a means by which to query
the RTL table version that lives in the Extended
BIOS Data Area (EBDA).
Users: The ibm-prtm userspace daemon uses this interface.

View file

@ -2646,10 +2646,10 @@ F: drivers/net/greth*
HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
M: Frank Seidel <frank@f-seidel.de>
L: lm-sensors@lm-sensors.org
L: platform-driver-x86@vger.kernel.org
W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/
S: Maintained
F: drivers/hwmon/hdaps.c
F: drivers/platform/x86/hdaps.c
HWPOISON MEMORY FAILURE HANDLING
M: Andi Kleen <andi@firstfloor.org>

View file

@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits);
/* EC commands */
#define EC_FIRMWARE_REV 0x08
#define EC_WLAN_ENTER_RESET 0x35
#define EC_WLAN_LEAVE_RESET 0x25
/* SCI source values */

View file

@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86
select INPUT_POLLDEV
default n
help
This driver provides support for the IBM Hard Drive Active Protection
System (hdaps), which provides an accelerometer and other misc. data.
ThinkPads starting with the R50, T41, and X40 are supported. The
accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
If your ThinkPad is not recognized by the driver, please update to latest
BIOS. This is especially the case for some R52 ThinkPads.
Say Y here if you have an applicable laptop and want to experience
the awesome power of hdaps.
config SENSORS_LIS3_SPI
tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)"
depends on !ACPI && SPI_MASTER && INPUT

View file

@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o

View file

@ -92,6 +92,7 @@ config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@ -140,6 +141,7 @@ config HP_WMI
depends on ACPI_WMI
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_SPARSEKMAP
help
Say Y here if you want to support WMI-based hotkeys on HP laptops and
to read data from WMI such as docking or ambient light sensor state.
@ -171,6 +173,7 @@ config PANASONIC_LAPTOP
tristate "Panasonic Laptop Extras"
depends on INPUT && ACPI
depends on BACKLIGHT_CLASS_DEVICE
select INPUT_SPARSEKMAP
---help---
This driver adds support for access to backlight control and hotkeys
on Panasonic Let's Note laptops.
@ -219,8 +222,8 @@ config SONYPI_COMPAT
---help---
Build the sonypi driver compatibility code into the sony-laptop driver.
config IDEAPAD_ACPI
tristate "Lenovo IdeaPad ACPI Laptop Extras"
config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI
depends on RFKILL
help
@ -365,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL
If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so.
config SENSORS_HDAPS
tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86
select INPUT_POLLDEV
default n
help
This driver provides support for the IBM Hard Drive Active Protection
System (hdaps), which provides an accelerometer and other misc. data.
ThinkPads starting with the R50, T41, and X40 are supported. The
accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing
the laptop to act as a pinball machine-esque joystick.
If your ThinkPad is not recognized by the driver, please update to latest
BIOS. This is especially the case for some R52 ThinkPads.
Say Y here if you have an applicable laptop and want to experience
the awesome power of hdaps.
config INTEL_MENLOW
tristate "Thermal Management driver for Intel menlow platform"
depends on ACPI_THERMAL
@ -478,6 +501,7 @@ config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
depends on INPUT
select INPUT_SPARSEKMAP
---help---
This driver adds support for hotkeys found on Topstar laptops.
@ -492,6 +516,7 @@ config ACPI_TOSHIBA
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_POLLDEV
select INPUT_SPARSEKMAP
---help---
This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by
@ -590,4 +615,28 @@ config INTEL_IPS
functionality. If in doubt, say Y here; it will only load on
supported platforms.
config IBM_RTL
tristate "Device driver to enable PRTL support"
depends on X86 && PCI
---help---
Enable support for IBM Premium Real Time Mode (PRTM).
This module will allow you the enter and exit PRTM in the BIOS via
sysfs on platforms that support this feature. System in PRTM will
not receive CPU-generated SMIs for recoverable errors. Use of this
feature without proper support may void your hardware warranty.
If the proper BIOS support is found the driver will load and create
/sys/devices/system/ibm_rtl/. The "state" variable will indicate
whether or not the BIOS is in PRTM.
state = 0 (BIOS SMIs on)
state = 1 (BIOS SMIs off)
config XO1_RFKILL
tristate "OLPC XO-1 software RF kill switch"
depends on OLPC
depends on RFKILL
---help---
Support for enabling/disabling the WLAN interface on the OLPC XO-1
laptop.
endif # X86_PLATFORM_DEVICES

View file

@ -15,8 +15,9 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
@ -30,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o

View file

@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void)
AMW0_find_mailled();
if (!interface) {
printk(ACER_ERR "No or unsupported WMI interface, unable to "
printk(ACER_INFO "No or unsupported WMI interface, unable to "
"load\n");
return -ENODEV;
}

View file

@ -236,7 +236,6 @@ struct asus_laptop {
u8 light_level; /* light sensor level */
u8 light_switch; /* light sensor switch value */
u16 event_count[128]; /* count for each event TODO make this better */
u16 *keycode_map;
};
static const struct key_entry asus_keymap[] = {
@ -278,6 +277,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x99, { KEY_PHONE } },
{KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
{KE_KEY, 0xb5, { KEY_CALC } },
{KE_END, 0},
};
@ -639,17 +639,19 @@ static int asus_backlight_notify(struct asus_laptop *asus)
static int asus_backlight_init(struct asus_laptop *asus)
{
struct backlight_device *bd;
struct device *dev = &asus->platform_device->dev;
struct backlight_properties props;
if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) &&
!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) &&
lcd_switch_handle) {
if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
!lcd_switch_handle)
return 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = 15;
bd = backlight_device_register(ASUS_LAPTOP_FILE, dev,
asus, &asusbl_ops, &props);
bd = backlight_device_register(ASUS_LAPTOP_FILE,
&asus->platform_device->dev, asus,
&asusbl_ops, &props);
if (IS_ERR(bd)) {
pr_err("Could not register asus backlight device\n");
asus->backlight_device = NULL;
@ -657,11 +659,9 @@ static int asus_backlight_init(struct asus_laptop *asus)
}
asus->backlight_device = bd;
bd->props.power = FB_BLANK_UNBLANK;
bd->props.brightness = asus_read_brightness(bd);
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd);
}
return 0;
}
@ -1065,9 +1065,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
*/
static int asus_gps_rfkill_set(void *data, bool blocked)
{
acpi_handle handle = data;
struct asus_laptop *asus = data;
return asus_gps_switch(handle, !blocked);
return asus_gps_switch(asus, !blocked);
}
static const struct rfkill_ops asus_gps_rfkill_ops = {
@ -1094,7 +1094,7 @@ static int asus_rfkill_init(struct asus_laptop *asus)
asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
RFKILL_TYPE_GPS,
&asus_gps_rfkill_ops, NULL);
&asus_gps_rfkill_ops, asus);
if (!asus->gps_rfkill)
return -EINVAL;
@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus)
input->phys = ASUS_LAPTOP_FILE "/input0";
input->id.bustype = BUS_HOST;
input->dev.parent = &asus->platform_device->dev;
input_set_drvdata(input, asus);
error = sparse_keymap_setup(input, asus_keymap, NULL);
if (error) {
@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus)
sparse_keymap_free(asus->inputdev);
input_unregister_device(asus->inputdev);
}
asus->inputdev = NULL;
}
/*
@ -1200,111 +1200,100 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL);
static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan);
static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth,
store_bluetooth);
static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
show_bluetooth, store_bluetooth);
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
static void asus_sysfs_exit(struct asus_laptop *asus)
static struct attribute *asus_attributes[] = {
&dev_attr_infos.attr,
&dev_attr_wlan.attr,
&dev_attr_bluetooth.attr,
&dev_attr_display.attr,
&dev_attr_ledd.attr,
&dev_attr_ls_level.attr,
&dev_attr_ls_switch.attr,
&dev_attr_gps.attr,
NULL
};
static mode_t asus_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr,
int idx)
{
struct platform_device *device = asus->platform_device;
struct device *dev = container_of(kobj, struct device, kobj);
struct platform_device *pdev = to_platform_device(dev);
struct asus_laptop *asus = platform_get_drvdata(pdev);
acpi_handle handle = asus->handle;
bool supported;
device_remove_file(&device->dev, &dev_attr_infos);
device_remove_file(&device->dev, &dev_attr_wlan);
device_remove_file(&device->dev, &dev_attr_bluetooth);
device_remove_file(&device->dev, &dev_attr_display);
device_remove_file(&device->dev, &dev_attr_ledd);
device_remove_file(&device->dev, &dev_attr_ls_switch);
device_remove_file(&device->dev, &dev_attr_ls_level);
device_remove_file(&device->dev, &dev_attr_gps);
if (attr == &dev_attr_wlan.attr) {
supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
} else if (attr == &dev_attr_bluetooth.attr) {
supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL);
} else if (attr == &dev_attr_display.attr) {
supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL);
} else if (attr == &dev_attr_ledd.attr) {
supported = !acpi_check_handle(handle, METHOD_LEDD, NULL);
} else if (attr == &dev_attr_ls_switch.attr ||
attr == &dev_attr_ls_level.attr) {
supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
!acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
} else if (attr == &dev_attr_gps.attr) {
supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
!acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
!acpi_check_handle(handle, METHOD_GPS_STATUS, NULL);
} else {
supported = true;
}
static int asus_sysfs_init(struct asus_laptop *asus)
{
struct platform_device *device = asus->platform_device;
int err;
err = device_create_file(&device->dev, &dev_attr_infos);
if (err)
return err;
if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) {
err = device_create_file(&device->dev, &dev_attr_wlan);
if (err)
return err;
return supported ? attr->mode : 0;
}
if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) {
err = device_create_file(&device->dev, &dev_attr_bluetooth);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) {
err = device_create_file(&device->dev, &dev_attr_display);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) {
err = device_create_file(&device->dev, &dev_attr_ledd);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
err = device_create_file(&device->dev, &dev_attr_ls_switch);
if (err)
return err;
err = device_create_file(&device->dev, &dev_attr_ls_level);
if (err)
return err;
}
if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
!acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
!acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) {
err = device_create_file(&device->dev, &dev_attr_gps);
if (err)
return err;
}
return err;
}
static const struct attribute_group asus_attr_group = {
.is_visible = asus_sysfs_is_visible,
.attrs = asus_attributes,
};
static int asus_platform_init(struct asus_laptop *asus)
{
int err;
int result;
asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1);
if (!asus->platform_device)
return -ENOMEM;
platform_set_drvdata(asus->platform_device, asus);
err = platform_device_add(asus->platform_device);
if (err)
result = platform_device_add(asus->platform_device);
if (result)
goto fail_platform_device;
err = asus_sysfs_init(asus);
if (err)
result = sysfs_create_group(&asus->platform_device->dev.kobj,
&asus_attr_group);
if (result)
goto fail_sysfs;
return 0;
fail_sysfs:
asus_sysfs_exit(asus);
platform_device_del(asus->platform_device);
fail_platform_device:
platform_device_put(asus->platform_device);
return err;
return result;
}
static void asus_platform_exit(struct asus_laptop *asus)
{
asus_sysfs_exit(asus);
sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group);
platform_device_unregister(asus->platform_device);
}
@ -1428,8 +1417,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
return AE_OK;
}
static bool asus_device_present;
static int __devinit asus_acpi_init(struct asus_laptop *asus)
{
int result = 0;
@ -1474,6 +1461,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
return result;
}
static bool asus_device_present;
static int __devinit asus_acpi_add(struct acpi_device *device)
{
struct asus_laptop *asus;

View file

@ -25,6 +25,8 @@
#include <linux/mm.h>
#include <linux/i8042.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = {
.query = dell_rfkill_query,
};
static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
{
int status;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
release_buffer();
seq_printf(s, "status:\t0x%X\n", status);
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
status & BIT(0));
seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
(status & BIT(1)) >> 1);
seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
(status & BIT(2)) >> 2);
seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
(status & BIT(3)) >> 3);
seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
(status & BIT(4)) >> 4);
seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
(status & BIT(5)) >> 5);
seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
(status & BIT(8)) >> 8);
seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
(status & BIT(9)) >> 9);
seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
(status & BIT(10)) >> 10);
seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
(status & BIT(16)) >> 16);
seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
(status & BIT(17)) >> 17);
seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
(status & BIT(18)) >> 18);
seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
(status & BIT(19)) >> 19);
seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
hwswitch_state & BIT(0));
seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
(hwswitch_state & BIT(1)) >> 1);
seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
(hwswitch_state & BIT(2)) >> 2);
seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
(hwswitch_state & BIT(7)) >> 7);
seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
(hwswitch_state & BIT(8)) >> 8);
seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
(hwswitch_state & BIT(15)) >> 15);
return 0;
}
static int dell_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, dell_debugfs_show, inode->i_private);
}
static const struct file_operations dell_debugfs_fops = {
.owner = THIS_MODULE,
.open = dell_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void dell_update_rfkill(struct work_struct *ignored)
{
if (wifi_rfkill)
@ -556,6 +627,11 @@ static int __init dell_init(void)
goto fail_filter;
}
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
#ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't
* register the platform controller.
@ -615,6 +691,7 @@ static int __init dell_init(void)
static void __exit dell_exit(void)
{
debugfs_remove_recursive(dell_laptop_dir);
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);

View file

@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <acpi/acpi_drivers.h>
#include <linux/acpi.h>
#include <linux/string.h>
@ -44,78 +45,70 @@ static int acpi_video;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
struct key_entry {
char type; /* See KE_* below */
u16 code;
u16 keycode;
};
enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
/*
* Certain keys are flagged as KE_IGNORE. All of these are either
* notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again.
*/
static struct key_entry dell_legacy_wmi_keymap[] = {
{KE_KEY, 0xe045, KEY_PROG1},
{KE_KEY, 0xe009, KEY_EJECTCD},
static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
{ KE_KEY, 0xe045, { KEY_PROG1 } },
{ KE_KEY, 0xe009, { KEY_EJECTCD } },
/* These also contain the brightness level at offset 6 */
{KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
{ KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
/* Battery health status button */
{KE_KEY, 0xe007, KEY_BATTERY},
{ KE_KEY, 0xe007, { KEY_BATTERY } },
/* This is actually for all radios. Although physically a
* switch, the notification does not provide an indication of
* state and so it should be reported as a key */
{KE_KEY, 0xe008, KEY_WLAN},
{ KE_KEY, 0xe008, { KEY_WLAN } },
/* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */
{KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
{ KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
{ KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } },
/* BIOS error detected */
{KE_IGNORE, 0xe00d, KEY_RESERVED},
{ KE_IGNORE, 0xe00d, { KEY_RESERVED } },
/* Wifi Catcher */
{KE_KEY, 0xe011, KEY_PROG2},
{ KE_KEY, 0xe011, {KEY_PROG2 } },
/* Ambient light sensor toggle */
{KE_IGNORE, 0xe013, KEY_RESERVED},
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
{KE_IGNORE, 0xe020, KEY_MUTE},
{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
{KE_IGNORE, 0xe030, KEY_VOLUMEUP},
{KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
{KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
{KE_IGNORE, 0xe045, KEY_NUMLOCK},
{KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
{ KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
{ KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
{ KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } },
{ KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
{ KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
{ KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
{ KE_END, 0 }
};
static bool dell_new_hk_type;
struct dell_new_keymap_entry {
struct dell_bios_keymap_entry {
u16 scancode;
u16 keycode;
};
struct dell_hotkey_table {
struct dell_bios_hotkey_table {
struct dmi_header header;
struct dell_new_keymap_entry keymap[];
struct dell_bios_keymap_entry keymap[];
};
static struct key_entry *dell_new_wmi_keymap;
static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
static u16 bios_to_linux_keycode[256] = {
static const u16 bios_to_linux_keycode[256] __initconst = {
KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = {
KEY_PROG3
};
static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
static struct input_dev *dell_wmi_input_dev;
static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (key->type == KE_KEY && keycode == key->keycode)
return key;
return NULL;
}
static int dell_wmi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int dell_wmi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!dell_wmi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void dell_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj;
acpi_status status;
@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER) {
const struct key_entry *key;
int reported_key;
u16 *buffer_entry = (u16 *)obj->buffer.pointer;
if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
printk(KERN_INFO "dell-wmi: Received unknown WMI event"
" (0x%x)\n", buffer_entry[1]);
@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context)
else
reported_key = (int)buffer_entry[1] & 0xffff;
key = dell_wmi_get_entry_by_scancode(reported_key);
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
reported_key);
if (!key) {
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
reported_key);
@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context)
* come via ACPI */
;
} else {
input_report_key(dell_wmi_input_dev, key->keycode, 1);
input_sync(dell_wmi_input_dev);
input_report_key(dell_wmi_input_dev, key->keycode, 0);
input_sync(dell_wmi_input_dev);
sparse_keymap_report_entry(dell_wmi_input_dev, key,
1, true);
}
}
kfree(obj);
}
static void setup_new_hk_map(const struct dmi_header *dm)
static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
{
int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
sizeof(struct dell_bios_keymap_entry);
struct key_entry *keymap;
int i;
int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
struct dell_hotkey_table *table =
container_of(dm, struct dell_hotkey_table, header);
dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
sizeof(struct key_entry), GFP_KERNEL);
keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
if (!keymap)
return NULL;
for (i = 0; i < hotkey_num; i++) {
dell_new_wmi_keymap[i].type = KE_KEY;
dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
dell_new_wmi_keymap[i].keycode =
(table->keymap[i].keycode > 255) ? 0 :
bios_to_linux_keycode[table->keymap[i].keycode];
const struct dell_bios_keymap_entry *bios_entry =
&dell_bios_hotkey_table->keymap[i];
keymap[i].type = KE_KEY;
keymap[i].code = bios_entry->scancode;
keymap[i].keycode = bios_entry->keycode < 256 ?
bios_to_linux_keycode[bios_entry->keycode] :
KEY_RESERVED;
}
dell_new_wmi_keymap[i].type = KE_END;
dell_new_wmi_keymap[i].code = 0;
dell_new_wmi_keymap[i].keycode = 0;
dell_wmi_keymap = dell_new_wmi_keymap;
keymap[hotkey_num].type = KE_END;
return keymap;
}
static void find_hk_type(const struct dmi_header *dm, void *dummy)
{
if ((dm->type == 0xb2) && (dm->length > 6)) {
dell_new_hk_type = true;
setup_new_hk_map(dm);
}
}
static int __init dell_wmi_input_setup(void)
{
struct key_entry *key;
int err;
dell_wmi_input_dev = input_allocate_device();
if (!dell_wmi_input_dev)
return -ENOMEM;
dell_wmi_input_dev->name = "Dell WMI hotkeys";
dell_wmi_input_dev->phys = "wmi/input0";
dell_wmi_input_dev->id.bustype = BUS_HOST;
dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode;
for (key = dell_wmi_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->keybit);
break;
case KE_SW:
set_bit(EV_SW, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->swbit);
break;
if (dell_new_hk_type) {
const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
if (!keymap) {
err = -ENOMEM;
goto err_free_dev;
}
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
/*
* Sparse keymap library makes a copy of keymap so we
* don't need the original one that was allocated.
*/
kfree(keymap);
} else {
err = sparse_keymap_setup(dell_wmi_input_dev,
dell_wmi_legacy_keymap, NULL);
}
if (err)
goto err_free_dev;
err = input_register_device(dell_wmi_input_dev);
if (err)
goto err_free_keymap;
if (err) {
return 0;
err_free_keymap:
sparse_keymap_free(dell_wmi_input_dev);
err_free_dev:
input_free_device(dell_wmi_input_dev);
return err;
}
return 0;
static void dell_wmi_input_destroy(void)
{
sparse_keymap_free(dell_wmi_input_dev);
input_unregister_device(dell_wmi_input_dev);
}
static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
{
if (dm->type == 0xb2 && dm->length > 6) {
dell_new_hk_type = true;
dell_bios_hotkey_table =
container_of(dm, struct dell_bios_hotkey_table, header);
}
}
static int __init dell_wmi_init(void)
@ -339,18 +283,13 @@ static int __init dell_wmi_init(void)
acpi_video = acpi_video_backlight_support();
err = dell_wmi_input_setup();
if (err) {
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
if (err)
return err;
}
status = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
input_unregister_device(dell_wmi_input_dev);
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
dell_wmi_input_destroy();
printk(KERN_ERR
"dell-wmi: Unable to register notify handler - %d\n",
status);
@ -359,14 +298,11 @@ static int __init dell_wmi_init(void)
return 0;
}
module_init(dell_wmi_init);
static void __exit dell_wmi_exit(void)
{
wmi_remove_notify_handler(DELL_EVENT_GUID);
input_unregister_device(dell_wmi_input_dev);
if (dell_new_hk_type)
kfree(dell_wmi_keymap);
dell_wmi_input_destroy();
}
module_init(dell_wmi_init);
module_exit(dell_wmi_exit);

View file

@ -165,6 +165,7 @@ struct eeepc_laptop {
u16 event_count[128]; /* count for each event */
struct platform_device *platform_device;
struct acpi_device *device; /* the device we are in */
struct device *hwmon_device;
struct backlight_device *backlight_device;
@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
sparse_keymap_free(eeepc->inputdev);
input_unregister_device(eeepc->inputdev);
}
eeepc->inputdev = NULL;
}
/*
@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
}
static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
struct acpi_device *device)
static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
{
unsigned int init_flags;
int result;
result = acpi_bus_get_status(device);
result = acpi_bus_get_status(eeepc->device);
if (result)
return result;
if (!device->status.present) {
if (!eeepc->device->status.present) {
pr_err("Hotkey device not present, aborting\n");
return -ENODEV;
}
@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
device->driver_data = eeepc;
eeepc->device = device;
eeepc->hotplug_disabled = hotplug_disabled;
eeepc_dmi_check(eeepc);
result = eeepc_acpi_init(eeepc, device);
result = eeepc_acpi_init(eeepc);
if (result)
goto fail_platform;
eeepc_enable_camera(eeepc);

View file

@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
#define EEEPC_WMI_METHODID_DEVS 0x53564544
#define EEEPC_WMI_METHODID_DSTS 0x53544344
#define EEEPC_WMI_METHODID_CFVS 0x53564643
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
@ -69,6 +70,11 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
{ KE_KEY, 0xe1, { KEY_F14 } },
{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
{ KE_KEY, 0xe0, { KEY_PROG1 } },
{ KE_KEY, 0x5c, { KEY_F15 } },
{ KE_END, 0},
};
@ -292,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context)
kfree(obj);
}
static int store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int value;
struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
acpi_status status;
if (!count || sscanf(buf, "%i", &value) != 1)
return -EINVAL;
if (value < 0 || value > 2)
return -EINVAL;
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
if (ACPI_FAILURE(status))
return -EIO;
else
return count;
}
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
static void eeepc_wmi_sysfs_exit(struct platform_device *device)
{
device_remove_file(&device->dev, &dev_attr_cpufv);
}
static int eeepc_wmi_sysfs_init(struct platform_device *device)
{
int retval = -ENOMEM;
retval = device_create_file(&device->dev, &dev_attr_cpufv);
if (retval)
goto error_sysfs;
return 0;
error_sysfs:
eeepc_wmi_sysfs_exit(platform_device);
return retval;
}
static int __devinit eeepc_wmi_platform_probe(struct platform_device *device)
{
struct eeepc_wmi *eeepc;
@ -387,8 +436,14 @@ static int __init eeepc_wmi_init(void)
goto del_dev;
}
err = eeepc_wmi_sysfs_init(platform_device);
if (err)
goto del_sysfs;
return 0;
del_sysfs:
eeepc_wmi_sysfs_exit(platform_device);
del_dev:
platform_device_del(platform_device);
put_dev:
@ -403,6 +458,7 @@ static void __exit eeepc_wmi_exit(void)
{
struct eeepc_wmi *eeepc;
eeepc_wmi_sysfs_exit(platform_device);
eeepc = platform_get_drvdata(platform_device);
platform_driver_unregister(&platform_driver);
platform_device_unregister(platform_device);

View file

@ -1,5 +1,5 @@
/*
* drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System
* hdaps.c - driver for IBM's Hard Drive Active Protection System
*
* Copyright (C) 2005 Robert Love <rml@novell.com>
* Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>

View file

@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
@ -88,23 +89,15 @@ struct bios_return {
u32 value;
};
struct key_entry {
char type; /* See KE_* below */
u16 code;
u16 keycode;
};
enum { KE_KEY, KE_END };
static struct key_entry hp_wmi_keymap[] = {
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x20e6, KEY_PROG1},
{KE_KEY, 0x20e8, KEY_MEDIA},
{KE_KEY, 0x2142, KEY_MEDIA},
{KE_KEY, 0x213b, KEY_INFO},
{KE_KEY, 0x2169, KEY_DIRECTION},
{KE_KEY, 0x231b, KEY_HELP},
static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x02, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x20e6, { KEY_PROG1 } },
{ KE_KEY, 0x20e8, { KEY_MEDIA } },
{ KE_KEY, 0x2142, { KEY_MEDIA } },
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
{ KE_KEY, 0x231b, { KEY_HELP } },
{ KE_END, 0 }
};
@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = hp_wmi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode)
{
struct key_entry *key;
for (key = hp_wmi_keymap; key->type != KE_END; key++)
if (key->type == KE_KEY && keycode == key->keycode)
return key;
return NULL;
}
static int hp_wmi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int hp_wmi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = hp_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!hp_wmi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void hp_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj;
u32 event_id, event_data;
int key_code = 0, ret;
@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context)
sizeof(key_code));
if (ret)
break;
key = hp_wmi_get_entry_by_scancode(key_code);
if (key) {
switch (key->type) {
case KE_KEY:
input_report_key(hp_wmi_input_dev,
key->keycode, 1);
input_sync(hp_wmi_input_dev);
input_report_key(hp_wmi_input_dev,
key->keycode, 0);
input_sync(hp_wmi_input_dev);
break;
}
} else
if (!sparse_keymap_report_event(hp_wmi_input_dev,
key_code, 1, true))
printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n",
key_code);
break;
@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context)
static int __init hp_wmi_input_setup(void)
{
struct key_entry *key;
acpi_status status;
int err;
hp_wmi_input_dev = input_allocate_device();
@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_input_dev->name = "HP WMI hotkeys";
hp_wmi_input_dev->phys = "wmi/input0";
hp_wmi_input_dev->id.bustype = BUS_HOST;
hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
for (key = hp_wmi_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
set_bit(key->keycode, hp_wmi_input_dev->keybit);
break;
}
}
__set_bit(EV_SW, hp_wmi_input_dev->evbit);
__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
if (err)
goto err_free_dev;
/* Set initial hardware state */
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
err = input_register_device(hp_wmi_input_dev);
status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
goto err_free_keymap;
}
if (err) {
err = input_register_device(hp_wmi_input_dev);
if (err)
goto err_uninstall_notifier;
return 0;
err_uninstall_notifier:
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(hp_wmi_input_dev);
err_free_dev:
input_free_device(hp_wmi_input_dev);
return err;
}
return 0;
static void hp_wmi_input_destroy(void)
{
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
sparse_keymap_free(hp_wmi_input_dev);
input_unregister_device(hp_wmi_input_dev);
}
static void cleanup_sysfs(struct platform_device *device)
@ -704,16 +643,10 @@ static int __init hp_wmi_init(void)
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
if (event_capable) {
err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
hp_wmi_notify, NULL);
if (ACPI_FAILURE(err))
return -EINVAL;
err = hp_wmi_input_setup();
if (err) {
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
if (err)
return err;
}
}
if (bios_capable) {
err = platform_driver_register(&hp_wmi_driver);
@ -739,20 +672,17 @@ static int __init hp_wmi_init(void)
err_device_alloc:
platform_driver_unregister(&hp_wmi_driver);
err_driver_reg:
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
input_unregister_device(hp_wmi_input_dev);
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
}
if (event_capable)
hp_wmi_input_destroy();
return err;
}
static void __exit hp_wmi_exit(void)
{
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
input_unregister_device(hp_wmi_input_dev);
}
if (wmi_has_guid(HPWMI_EVENT_GUID))
hp_wmi_input_destroy();
if (hp_wmi_platform_dev) {
platform_device_unregister(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver);

View file

@ -0,0 +1,341 @@
/*
* IBM Real-Time Linux driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2010
*
* Author: Keith Mannthey <kmannth@us.ibm.com>
* Vernon Mauery <vernux@us.ibm.com>
*
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/sysdev.h>
#include <linux/dmi.h>
#include <linux/mutex.h>
#include <asm/bios_ebda.h>
static bool force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Show debug output");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
#define RTL_ADDR_TYPE_IO 1
#define RTL_ADDR_TYPE_MMIO 2
#define RTL_CMD_ENTER_PRTM 1
#define RTL_CMD_EXIT_PRTM 2
/* The RTL table as presented by the EBDA: */
struct ibm_rtl_table {
char signature[5]; /* signature should be "_RTL_" */
u8 version;
u8 rt_status;
u8 command;
u8 command_status;
u8 cmd_address_type;
u8 cmd_granularity;
u8 cmd_offset;
u16 reserve1;
u32 cmd_port_address; /* platform dependent address */
u32 cmd_port_value; /* platform dependent value */
} __attribute__((packed));
/* to locate "_RTL_" signature do a masked 5-byte integer compare */
#define RTL_SIGNATURE 0x0000005f4c54525fULL
#define RTL_MASK 0x000000ffffffffffULL
#define RTL_DEBUG(A, ...) do { \
if (debug) \
pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \
} while (0)
static DEFINE_MUTEX(rtl_lock);
static struct ibm_rtl_table __iomem *rtl_table;
static void __iomem *ebda_map;
static void __iomem *rtl_cmd_addr;
static u8 rtl_cmd_type;
static u8 rtl_cmd_width;
static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
{
if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
return ioremap(addr, len);
return ioport_map(addr, len);
}
static void rtl_port_unmap(void __iomem *addr)
{
if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
iounmap(addr);
else
ioport_unmap(addr);
}
static int ibm_rtl_write(u8 value)
{
int ret = 0, count = 0;
static u32 cmd_port_val;
RTL_DEBUG("%s(%d)\n", __FUNCTION__, value);
value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;
mutex_lock(&rtl_lock);
if (ioread8(&rtl_table->rt_status) != value) {
iowrite8(value, &rtl_table->command);
switch (rtl_cmd_width) {
case 8:
cmd_port_val = ioread8(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite8((u8)cmd_port_val, rtl_cmd_addr);
break;
case 16:
cmd_port_val = ioread16(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite16((u16)cmd_port_val, rtl_cmd_addr);
break;
case 32:
cmd_port_val = ioread32(&rtl_table->cmd_port_value);
RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);
iowrite32(cmd_port_val, rtl_cmd_addr);
break;
}
while (ioread8(&rtl_table->command)) {
msleep(10);
if (count++ > 500) {
pr_err("ibm-rtl: Hardware not responding to "
"mode switch request\n");
ret = -EIO;
break;
}
}
if (ioread8(&rtl_table->command_status)) {
RTL_DEBUG("command_status reports failed command\n");
ret = -EIO;
}
}
mutex_unlock(&rtl_lock);
return ret;
}
static ssize_t rtl_show_version(struct sysdev_class * dev,
struct sysdev_class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));
}
static ssize_t rtl_show_state(struct sysdev_class *dev,
struct sysdev_class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));
}
static ssize_t rtl_set_state(struct sysdev_class *dev,
struct sysdev_class_attribute *attr,
const char *buf,
size_t count)
{
ssize_t ret;
if (count < 1 || count > 2)
return -EINVAL;
switch (buf[0]) {
case '0':
ret = ibm_rtl_write(0);
break;
case '1':
ret = ibm_rtl_write(1);
break;
default:
ret = -EINVAL;
}
if (ret >= 0)
ret = count;
return ret;
}
static struct sysdev_class class_rtl = {
.name = "ibm_rtl",
};
static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL);
static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state);
static struct sysdev_class_attribute *rtl_attributes[] = {
&attr_version,
&attr_state,
NULL
};
static int rtl_setup_sysfs(void) {
int ret, i;
ret = sysdev_class_register(&class_rtl);
if (!ret) {
for (i = 0; rtl_attributes[i]; i ++)
sysdev_class_create_file(&class_rtl, rtl_attributes[i]);
}
return ret;
}
static void rtl_teardown_sysfs(void) {
int i;
for (i = 0; rtl_attributes[i]; i ++)
sysdev_class_remove_file(&class_rtl, rtl_attributes[i]);
sysdev_class_unregister(&class_rtl);
}
static int dmi_check_cb(const struct dmi_system_id *id)
{
RTL_DEBUG("found IBM server '%s'\n", id->ident);
return 0;
}
#define ibm_dmi_entry(NAME, TYPE) \
{ \
.ident = NAME, \
.matches = { \
DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \
}, \
.callback = dmi_check_cb \
}
static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
ibm_dmi_entry("BladeCenter LS21", "7971"),
ibm_dmi_entry("BladeCenter LS22", "7901"),
ibm_dmi_entry("BladeCenter HS21 XM", "7995"),
ibm_dmi_entry("BladeCenter HS22", "7870"),
ibm_dmi_entry("BladeCenter HS22V", "7871"),
ibm_dmi_entry("System x3550 M2", "7946"),
ibm_dmi_entry("System x3650 M2", "7947"),
ibm_dmi_entry("System x3550 M3", "7944"),
ibm_dmi_entry("System x3650 M3", "7945"),
{ }
};
static int __init ibm_rtl_init(void) {
unsigned long ebda_addr, ebda_size;
unsigned int ebda_kb;
int ret = -ENODEV, i;
if (force)
pr_warning("ibm-rtl: module loaded by force\n");
/* first ensure that we are running on IBM HW */
else if (!dmi_check_system(ibm_rtl_dmi_table))
return -ENODEV;
/* Get the address for the Extended BIOS Data Area */
ebda_addr = get_bios_ebda();
if (!ebda_addr) {
RTL_DEBUG("no BIOS EBDA found\n");
return -ENODEV;
}
ebda_map = ioremap(ebda_addr, 4);
if (!ebda_map)
return -ENOMEM;
/* First word in the EDBA is the Size in KB */
ebda_kb = ioread16(ebda_map);
RTL_DEBUG("EBDA is %d kB\n", ebda_kb);
if (ebda_kb == 0)
goto out;
iounmap(ebda_map);
ebda_size = ebda_kb*1024;
/* Remap the whole table */
ebda_map = ioremap(ebda_addr, ebda_size);
if (!ebda_map)
return -ENOMEM;
/* search for the _RTL_ signature at the start of the table */
for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {
struct ibm_rtl_table __iomem * tmp;
tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);
if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {
phys_addr_t addr;
unsigned int plen;
RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp);
rtl_table = tmp;
/* The address, value, width and offset are platform
* dependent and found in the ibm_rtl_table */
rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);
rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);
RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",
rtl_cmd_width, rtl_cmd_type);
addr = ioread32(&rtl_table->cmd_port_address);
RTL_DEBUG("addr = %#llx\n", addr);
plen = rtl_cmd_width/sizeof(char);
rtl_cmd_addr = rtl_port_map(addr, plen);
RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr);
if (!rtl_cmd_addr) {
ret = -ENOMEM;
break;
}
ret = rtl_setup_sysfs();
break;
}
}
out:
if (ret) {
iounmap(ebda_map);
rtl_port_unmap(rtl_cmd_addr);
}
return ret;
}
static void __exit ibm_rtl_exit(void)
{
if (rtl_table) {
RTL_DEBUG("cleaning up");
/* do not leave the machine in SMI-free mode */
ibm_rtl_write(0);
/* unmap, unlink and remove all traces */
rtl_teardown_sysfs();
iounmap(ebda_map);
rtl_port_unmap(rtl_cmd_addr);
}
}
module_init(ibm_rtl_init);
module_exit(ibm_rtl_exit);

View file

@ -35,112 +35,162 @@
#define IDEAPAD_DEV_KILLSW 4
struct ideapad_private {
acpi_handle handle;
struct rfkill *rfk[5];
};
} *ideapad_priv;
static struct {
char *name;
int cfgbit;
int opcode;
int type;
} ideapad_rfk_data[] = {
/* camera has no rfkill */
{ "ideapad_wlan", RFKILL_TYPE_WLAN },
{ "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH },
{ "ideapad_3g", RFKILL_TYPE_WWAN },
{ "ideapad_killsw", RFKILL_TYPE_WLAN }
{ "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES },
{ "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN },
{ "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH },
{ "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN },
{ "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN }
};
static int ideapad_dev_exists(int device)
static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
/*
* ACPI Helpers
*/
#define IDEAPAD_EC_TIMEOUT (100) /* in ms */
static int read_method_int(acpi_handle handle, const char *method, int *val)
{
acpi_status status;
union acpi_object in_param;
struct acpi_object_list input = { 1, &in_param };
struct acpi_buffer output;
union acpi_object out_obj;
unsigned long long result;
output.length = sizeof(out_obj);
output.pointer = &out_obj;
in_param.type = ACPI_TYPE_INTEGER;
in_param.integer.value = device + 1;
status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
if (ACPI_FAILURE(status)) {
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
return -ENODEV;
}
if (out_obj.type != ACPI_TYPE_INTEGER) {
printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
return -ENODEV;
}
return out_obj.integer.value;
}
static int ideapad_dev_get_state(int device)
{
acpi_status status;
union acpi_object in_param;
struct acpi_object_list input = { 1, &in_param };
struct acpi_buffer output;
union acpi_object out_obj;
output.length = sizeof(out_obj);
output.pointer = &out_obj;
in_param.type = ACPI_TYPE_INTEGER;
in_param.integer.value = device + 1;
status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
if (ACPI_FAILURE(status)) {
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
return -ENODEV;
}
if (out_obj.type != ACPI_TYPE_INTEGER) {
printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
return -ENODEV;
}
return out_obj.integer.value;
}
static int ideapad_dev_set_state(int device, int state)
{
acpi_status status;
union acpi_object in_params[2];
struct acpi_object_list input = { 2, in_params };
in_params[0].type = ACPI_TYPE_INTEGER;
in_params[0].integer.value = device + 1;
in_params[1].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = state;
status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
if (ACPI_FAILURE(status)) {
printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
return -ENODEV;
}
*val = -1;
return -1;
} else {
*val = result;
return 0;
}
}
static int method_vpcr(acpi_handle handle, int cmd, int *ret)
{
acpi_status status;
unsigned long long result;
struct acpi_object_list params;
union acpi_object in_obj;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = cmd;
status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
if (ACPI_FAILURE(status)) {
*ret = -1;
return -1;
} else {
*ret = result;
return 0;
}
}
static int method_vpcw(acpi_handle handle, int cmd, int data)
{
struct acpi_object_list params;
union acpi_object in_obj[2];
acpi_status status;
params.count = 2;
params.pointer = in_obj;
in_obj[0].type = ACPI_TYPE_INTEGER;
in_obj[0].integer.value = cmd;
in_obj[1].type = ACPI_TYPE_INTEGER;
in_obj[1].integer.value = data;
status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
if (status != AE_OK)
return -1;
return 0;
}
static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
{
int val;
unsigned long int end_jiffies;
if (method_vpcw(handle, 1, cmd))
return -1;
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
time_before(jiffies, end_jiffies);) {
schedule();
if (method_vpcr(handle, 1, &val))
return -1;
if (val == 0) {
if (method_vpcr(handle, 0, &val))
return -1;
*data = val;
return 0;
}
}
pr_err("timeout in read_ec_cmd\n");
return -1;
}
static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
{
int val;
unsigned long int end_jiffies;
if (method_vpcw(handle, 0, data))
return -1;
if (method_vpcw(handle, 1, cmd))
return -1;
for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
time_before(jiffies, end_jiffies);) {
schedule();
if (method_vpcr(handle, 1, &val))
return -1;
if (val == 0)
return 0;
}
pr_err("timeout in write_ec_cmd\n");
return -1;
}
/* the above is ACPI helpers */
static ssize_t show_ideapad_cam(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
if (state < 0)
return state;
struct ideapad_private *priv = dev_get_drvdata(dev);
acpi_handle handle = priv->handle;
unsigned long result;
return sprintf(buf, "%d\n", state);
if (read_ec_data(handle, 0x1D, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
}
static ssize_t store_ideapad_cam(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
acpi_handle handle = priv->handle;
int ret, state;
if (!count)
return 0;
if (sscanf(buf, "%i", &state) != 1)
return -EINVAL;
ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state);
ret = write_ec_cmd(handle, 0x1E, state);
if (ret < 0)
return ret;
return count;
@ -154,7 +204,10 @@ static int ideapad_rfk_set(void *data, bool blocked)
if (device == IDEAPAD_DEV_KILLSW)
return -EINVAL;
return ideapad_dev_set_state(device, !blocked);
return write_ec_cmd(ideapad_priv->handle,
ideapad_rfk_data[device].opcode,
!blocked);
}
static struct rfkill_ops ideapad_rfk_ops = {
@ -164,32 +217,47 @@ static struct rfkill_ops ideapad_rfk_ops = {
static void ideapad_sync_rfk_state(struct acpi_device *adevice)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
acpi_handle handle = priv->handle;
unsigned long hw_blocked;
int i;
rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked);
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
if (read_ec_data(handle, 0x23, &hw_blocked))
return;
hw_blocked = !hw_blocked;
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++)
if (priv->rfk[i])
rfkill_set_hw_state(priv->rfk[i], hw_blocked);
if (hw_blocked)
return;
for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
if (priv->rfk[i])
rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i));
}
static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)
{
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int ret;
unsigned long sw_blocked;
priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev,
ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops,
if (no_bt_rfkill &&
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
/* Force to enable bluetooth when no_bt_rfkill=1 */
write_ec_cmd(ideapad_priv->handle,
ideapad_rfk_data[dev].opcode, 1);
return 0;
}
priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
(void *)(long)dev);
if (!priv->rfk[dev])
return -ENOMEM;
if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1,
&sw_blocked)) {
rfkill_init_sw_state(priv->rfk[dev], 0);
} else {
sw_blocked = !sw_blocked;
rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
}
ret = rfkill_register(priv->rfk[dev]);
if (ret) {
rfkill_destroy(priv->rfk[dev]);
@ -217,14 +285,18 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
static int ideapad_acpi_add(struct acpi_device *adevice)
{
int i;
int i, cfg;
int devs_present[5];
struct ideapad_private *priv;
if (read_method_int(adevice->handle, "_CFG", &cfg))
return -ENODEV;
for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
devs_present[i] = ideapad_dev_exists(i);
if (devs_present[i] < 0)
return devs_present[i];
if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
devs_present[i] = 1;
else
devs_present[i] = 0;
}
/* The hardware switch is always present */
@ -242,7 +314,9 @@ static int ideapad_acpi_add(struct acpi_device *adevice)
}
}
priv->handle = adevice->handle;
dev_set_drvdata(&adevice->dev, priv);
ideapad_priv = priv;
for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
if (!devs_present[i])
continue;
@ -270,8 +344,22 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type)
static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
{
acpi_handle handle = adevice->handle;
unsigned long vpc1, vpc2, vpc_bit;
if (read_ec_data(handle, 0x10, &vpc1))
return;
if (read_ec_data(handle, 0x1A, &vpc2))
return;
vpc1 = (vpc2 << 8) | vpc1;
for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
if (test_bit(vpc_bit, &vpc1)) {
if (vpc_bit == 9)
ideapad_sync_rfk_state(adevice);
}
}
}
static struct acpi_driver ideapad_acpi_driver = {
.name = "ideapad_acpi",

View file

@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip,
if (offset < 8)/* it is GPIO */
rc = intel_scu_ipc_update_register(GPIO0 + offset,
GPIO_DRV | GPIO_DOU | GPIO_DIR,
GPIO_DRV | (value ? GPIO_DOU : 0));
GPIO_DRV | (value ? GPIO_DOU : 0),
GPIO_DRV | GPIO_DOU | GPIO_DIR);
else if (offset < 16)/* it is GPOSW */
rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
GPOSW_DRV | (value ? GPOSW_DOU : 0));
GPOSW_DRV | (value ? GPOSW_DOU : 0),
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24)/* it is GPO */
rc = intel_scu_ipc_update_register(GPO,
1 << (offset - 16),
value ? 1 << (offset - 16) : 0);
value ? 1 << (offset - 16) : 0,
1 << (offset - 16));
else {
printk(KERN_ERR
"%s: invalid PMIC GPIO pin %d!\n", __func__, offset);
@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
if (offset < 8)/* it is GPIO */
intel_scu_ipc_update_register(GPIO0 + offset,
GPIO_DRV | GPIO_DOU,
GPIO_DRV | (value ? GPIO_DOU : 0));
GPIO_DRV | (value ? GPIO_DOU : 0),
GPIO_DRV | GPIO_DOU);
else if (offset < 16)/* it is GPOSW */
intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV,
GPOSW_DRV | (value ? GPOSW_DOU : 0));
GPOSW_DRV | (value ? GPOSW_DOU : 0),
GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
else if (offset > 15 && offset < 24) /* it is GPO */
intel_scu_ipc_update_register(GPO,
1 << (offset - 16),
value ? 1 << (offset - 16) : 0);
value ? 1 << (offset - 16) : 0,
1 << (offset - 16));
}
static int pmic_irq_type(unsigned irq, unsigned type)
@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type)
u32 gpio = irq - pg->irq_base;
unsigned long flags;
if (gpio > pg->chip.ngpio)
if (gpio >= pg->chip.ngpio)
return -EINVAL;
spin_lock_irqsave(&pg->irqtypes.lock, flags);

View file

@ -23,6 +23,7 @@
#include <linux/pm.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sfi.h>
#include <asm/mrst.h>
#include <asm/intel_scu_ipc.h>

View file

@ -128,6 +128,7 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#ifndef ACPI_HOTKEY_COMPONENT
@ -200,19 +201,19 @@ static struct acpi_driver acpi_pcc_driver = {
},
};
#define KEYMAP_SIZE 11
static const unsigned int initial_keymap[KEYMAP_SIZE] = {
/* 0 */ KEY_RESERVED,
/* 1 */ KEY_BRIGHTNESSDOWN,
/* 2 */ KEY_BRIGHTNESSUP,
/* 3 */ KEY_DISPLAYTOGGLE,
/* 4 */ KEY_MUTE,
/* 5 */ KEY_VOLUMEDOWN,
/* 6 */ KEY_VOLUMEUP,
/* 7 */ KEY_SLEEP,
/* 8 */ KEY_PROG1, /* Change CPU boost */
/* 9 */ KEY_BATTERY,
/* 10 */ KEY_SUSPEND,
static const struct key_entry panasonic_keymap[] = {
{ KE_KEY, 0, { KEY_RESERVED } },
{ KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 2, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
{ KE_KEY, 4, { KEY_MUTE } },
{ KE_KEY, 5, { KEY_VOLUMEDOWN } },
{ KE_KEY, 6, { KEY_VOLUMEUP } },
{ KE_KEY, 7, { KEY_SLEEP } },
{ KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
{ KE_KEY, 9, { KEY_BATTERY } },
{ KE_KEY, 10, { KEY_SUSPEND } },
{ KE_END, 0 }
};
struct pcc_acpi {
@ -223,7 +224,6 @@ struct pcc_acpi {
struct acpi_device *device;
struct input_dev *input_dev;
struct backlight_device *backlight;
unsigned int keymap[KEYMAP_SIZE];
};
struct pcc_keyinput {
@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device)
}
}
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
{
acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
hkey = buffer.pointer;
if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
status = AE_ERROR;
goto end;
}
@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
for (i = 0; i < hkey->package.count; i++) {
union acpi_object *element = &(hkey->package.elements[i]);
if (likely(element->type == ACPI_TYPE_INTEGER)) {
sinf[i] = element->integer.value;
pcc->sinf[i] = element->integer.value;
} else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid HKEY.SINF data\n"));
}
sinf[hkey->package.count] = -1;
pcc->sinf[hkey->package.count] = -1;
end:
kfree(buffer.pointer);
@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd)
{
struct pcc_acpi *pcc = bl_get_data(bd);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return pcc->sinf[SINF_AC_CUR_BRIGHT];
@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd)
int bright = bd->props.brightness;
int rc;
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
if (!acpi_pcc_retrieve_biosdata(pcc))
return -EIO;
return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */
static int pcc_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct pcc_acpi *pcc = input_get_drvdata(dev);
if (scancode >= ARRAY_SIZE(pcc->keymap))
return -EINVAL;
*keycode = pcc->keymap[scancode];
return 0;
}
static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
if (pcc->keymap[i] == keycode)
return i+1;
}
return 0;
}
static int pcc_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct pcc_acpi *pcc = input_get_drvdata(dev);
int oldkeycode;
if (scancode >= ARRAY_SIZE(pcc->keymap))
return -EINVAL;
oldkeycode = pcc->keymap[scancode];
pcc->keymap[scancode] = keycode;
set_bit(keycode, dev->keybit);
if (!keymap_get_by_keycode(pcc, oldkeycode))
clear_bit(oldkeycode, dev->keybit);
return 0;
}
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{
struct input_dev *hotk_input_dev = pcc->input_dev;
int rc;
int key_code, hkey_num;
unsigned long long result;
rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
hkey_num = result & 0xf;
if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) {
if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false))
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"hotkey number out of range: %d\n",
hkey_num));
return;
}
key_code = pcc->keymap[hkey_num];
if (key_code != KEY_RESERVED) {
int pushed = (result & 0x80) ? TRUE : FALSE;
input_report_key(hotk_input_dev, key_code, pushed);
input_sync(hotk_input_dev);
}
return;
"Unknown hotkey event: %d\n", result));
}
static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
static int acpi_pcc_init_input(struct pcc_acpi *pcc)
{
int i, rc;
struct input_dev *input_dev;
int error;
pcc->input_dev = input_allocate_device();
if (!pcc->input_dev) {
input_dev = input_allocate_device();
if (!input_dev) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't allocate input device for hotkey"));
return -ENOMEM;
}
pcc->input_dev->evbit[0] = BIT(EV_KEY);
input_dev->name = ACPI_PCC_DRIVER_NAME;
input_dev->phys = ACPI_PCC_INPUT_PHYS;
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
pcc->input_dev->id.bustype = BUS_HOST;
pcc->input_dev->id.vendor = 0x0001;
pcc->input_dev->id.product = 0x0001;
pcc->input_dev->id.version = 0x0100;
pcc->input_dev->getkeycode = pcc_getkeycode;
pcc->input_dev->setkeycode = pcc_setkeycode;
error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
if (error) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to setup input device keymap\n"));
goto err_free_dev;
}
/* load initial keymap */
memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
error = input_register_device(input_dev);
if (error) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to register input device\n"));
goto err_free_keymap;
}
for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
__set_bit(pcc->keymap[i], pcc->input_dev->keybit);
__clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
pcc->input_dev = input_dev;
return 0;
input_set_drvdata(pcc->input_dev, pcc);
err_free_keymap:
sparse_keymap_free(input_dev);
err_free_dev:
input_free_device(input_dev);
return error;
}
rc = input_register_device(pcc->input_dev);
if (rc < 0)
input_free_device(pcc->input_dev);
return rc;
static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
{
sparse_keymap_free(pcc->input_dev);
input_unregister_device(pcc->input_dev);
/*
* No need to input_free_device() since core input API refcounts
* and free()s the device.
*/
}
/* kernel module interface */
@ -636,12 +591,13 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error installing keyinput handler\n"));
goto out_hotkey;
goto out_sinf;
}
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
if (!acpi_pcc_retrieve_biosdata(pcc)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't retrieve BIOS data\n"));
result = -EIO;
goto out_input;
}
/* initialize backlight */
@ -651,7 +607,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
&pcc_backlight_ops, &props);
if (IS_ERR(pcc->backlight)) {
result = PTR_ERR(pcc->backlight);
goto out_sinf;
goto out_input;
}
/* read the initial brightness setting from the hardware */
@ -669,12 +625,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
out_backlight:
backlight_device_unregister(pcc->backlight);
out_input:
acpi_pcc_destroy_input(pcc);
out_sinf:
kfree(pcc->sinf);
out_input:
input_unregister_device(pcc->input_dev);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
out_hotkey:
kfree(pcc);
@ -709,9 +663,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
backlight_device_unregister(pcc->backlight);
input_unregister_device(pcc->input_dev);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
acpi_pcc_destroy_input(pcc);
kfree(pcc->sinf);
kfree(pcc);

View file

@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#define ACPI_TOPSTAR_CLASS "topstar"
@ -26,52 +27,37 @@ struct topstar_hkey {
struct input_dev *inputdev;
};
struct tps_key_entry {
u8 code;
u16 keycode;
static const struct key_entry topstar_keymap[] = {
{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x83, { KEY_VOLUMEUP } },
{ KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
{ KE_KEY, 0x85, { KEY_MUTE } },
{ KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
{ KE_KEY, 0x88, { KEY_WLAN } },
{ KE_KEY, 0x8a, { KEY_WWW } },
{ KE_KEY, 0x8b, { KEY_MAIL } },
{ KE_KEY, 0x8c, { KEY_MEDIA } },
/* Known non hotkey events don't handled or that we don't care yet */
{ KE_IGNORE, 0x8e, },
{ KE_IGNORE, 0x8f, },
{ KE_IGNORE, 0x90, },
/*
* 'G key' generate two event codes, convert to only
* one event/key code for now, consider replacing by
* a switch (3G switch - SW_3G?)
*/
{ KE_KEY, 0x96, { KEY_F14 } },
{ KE_KEY, 0x97, { KEY_F14 } },
{ KE_END, 0 }
};
static struct tps_key_entry topstar_keymap[] = {
{ 0x80, KEY_BRIGHTNESSUP },
{ 0x81, KEY_BRIGHTNESSDOWN },
{ 0x83, KEY_VOLUMEUP },
{ 0x84, KEY_VOLUMEDOWN },
{ 0x85, KEY_MUTE },
{ 0x86, KEY_SWITCHVIDEOMODE },
{ 0x87, KEY_F13 }, /* touchpad enable/disable key */
{ 0x88, KEY_WLAN },
{ 0x8a, KEY_WWW },
{ 0x8b, KEY_MAIL },
{ 0x8c, KEY_MEDIA },
{ 0x96, KEY_F14 }, /* G key? */
{ }
};
static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code)
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++)
if (code == key->code)
return key;
return NULL;
}
static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code)
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++)
if (code == key->keycode)
return key;
return NULL;
}
static void acpi_topstar_notify(struct acpi_device *device, u32 event)
{
struct tps_key_entry *key;
static bool dup_evnt[2];
bool *dup;
struct topstar_hkey *hkey = acpi_driver_data(device);
@ -86,26 +72,7 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)
*dup = true;
}
/*
* 'G key' generate two event codes, convert to only
* one event/key code for now (3G switch?)
*/
if (event == 0x97)
event = 0x96;
key = tps_get_key_by_scancode(event);
if (key) {
input_report_key(hkey->inputdev, key->keycode, 1);
input_sync(hkey->inputdev);
input_report_key(hkey->inputdev, key->keycode, 0);
input_sync(hkey->inputdev);
return;
}
/* Known non hotkey events don't handled or that we don't care yet */
if (event == 0x8e || event == 0x8f || event == 0x90)
return;
if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
pr_info("unknown event = 0x%02x\n", event);
}
@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
return 0;
}
static int topstar_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
*keycode = key->keycode;
return 0;
}
static int topstar_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct tps_key_entry *key;
int old_keycode;
key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!tps_get_key_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
{
struct tps_key_entry *key;
struct input_dev *input;
int error;
hkey->inputdev = input_allocate_device();
if (!hkey->inputdev) {
input = input_allocate_device();
if (!input) {
pr_err("Unable to allocate input device\n");
return -ENODEV;
}
hkey->inputdev->name = "Topstar Laptop extra buttons";
hkey->inputdev->phys = "topstar/input0";
hkey->inputdev->id.bustype = BUS_HOST;
hkey->inputdev->getkeycode = topstar_getkeycode;
hkey->inputdev->setkeycode = topstar_setkeycode;
for (key = topstar_keymap; key->code; key++) {
set_bit(EV_KEY, hkey->inputdev->evbit);
set_bit(key->keycode, hkey->inputdev->keybit);
}
if (input_register_device(hkey->inputdev)) {
pr_err("Unable to register input device\n");
input_free_device(hkey->inputdev);
return -ENODEV;
return -ENOMEM;
}
input->name = "Topstar Laptop extra buttons";
input->phys = "topstar/input0";
input->id.bustype = BUS_HOST;
error = sparse_keymap_setup(input, topstar_keymap, NULL);
if (error) {
pr_err("Unable to setup input device keymap\n");
goto err_free_dev;
}
error = input_register_device(input);
if (error) {
pr_err("Unable to register input device\n");
goto err_free_keymap;
}
hkey->inputdev = input;
return 0;
err_free_keymap:
sparse_keymap_free(input);
err_free_dev:
input_free_device(input);
return error;
}
static int acpi_topstar_add(struct acpi_device *device)
@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type)
acpi_topstar_fncx_switch(device, false);
sparse_keymap_free(tps_hkey->inputdev);
input_unregister_device(tps_hkey->inputdev);
kfree(tps_hkey);

View file

@ -48,6 +48,7 @@
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/slab.h>
@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
struct key_entry {
char type;
u16 code;
u16 keycode;
};
enum {KE_KEY, KE_END};
static struct key_entry toshiba_acpi_keymap[] = {
{KE_KEY, 0x101, KEY_MUTE},
{KE_KEY, 0x102, KEY_ZOOMOUT},
{KE_KEY, 0x103, KEY_ZOOMIN},
{KE_KEY, 0x13b, KEY_COFFEE},
{KE_KEY, 0x13c, KEY_BATTERY},
{KE_KEY, 0x13d, KEY_SLEEP},
{KE_KEY, 0x13e, KEY_SUSPEND},
{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
{KE_KEY, 0x142, KEY_WLAN},
{KE_KEY, 0x143, KEY_PROG1},
{KE_KEY, 0xb05, KEY_PROG2},
{KE_KEY, 0xb06, KEY_WWW},
{KE_KEY, 0xb07, KEY_MAIL},
{KE_KEY, 0xb30, KEY_STOP},
{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
{KE_KEY, 0xb32, KEY_NEXTSONG},
{KE_KEY, 0xb33, KEY_PLAYPAUSE},
{KE_KEY, 0xb5a, KEY_MEDIA},
{KE_END, 0, 0},
static const struct key_entry toshiba_acpi_keymap[] __initconst = {
{ KE_KEY, 0x101, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
{ KE_KEY, 0x13b, { KEY_COFFEE } },
{ KE_KEY, 0x13c, { KEY_BATTERY } },
{ KE_KEY, 0x13d, { KEY_SLEEP } },
{ KE_KEY, 0x13e, { KEY_SUSPEND } },
{ KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x142, { KEY_WLAN } },
{ KE_KEY, 0x143, { KEY_PROG1 } },
{ KE_KEY, 0xb05, { KEY_PROG2 } },
{ KE_KEY, 0xb06, { KEY_WWW } },
{ KE_KEY, 0xb07, { KEY_MAIL } },
{ KE_KEY, 0xb30, { KEY_STOP } },
{ KE_KEY, 0xb31, { KEY_PREVIOUSSONG } },
{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
{ KE_KEY, 0xb5a, { KEY_MEDIA } },
{ KE_END, 0 },
};
/* utility
@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code)
{
struct key_entry *key;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code)
{
struct key_entry *key;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
if (code == key->keycode && key->type == KE_KEY)
return key;
return NULL;
}
static int toshiba_acpi_getkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode)
{
struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int toshiba_acpi_setkeycode(struct input_dev *dev,
unsigned int scancode, unsigned int keycode)
{
struct key_entry *key;
unsigned int old_keycode;
key = toshiba_acpi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
{
u32 hci_result, value;
struct key_entry *key;
if (event != 0x80)
return;
@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
if (value & 0x80)
continue;
key = toshiba_acpi_get_entry_by_scancode
(value);
if (!key) {
if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev,
value, 1, true)) {
printk(MY_INFO "Unknown key %x\n",
value);
continue;
}
input_report_key(toshiba_acpi.hotkey_dev,
key->keycode, 1);
input_sync(toshiba_acpi.hotkey_dev);
input_report_key(toshiba_acpi.hotkey_dev,
key->keycode, 0);
input_sync(toshiba_acpi.hotkey_dev);
} else if (hci_result == HCI_NOT_SUPPORTED) {
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context)
} while (hci_result != HCI_EMPTY);
}
static int toshiba_acpi_setup_keyboard(char *device)
static int __init toshiba_acpi_setup_keyboard(char *device)
{
acpi_status status;
acpi_handle handle;
int result;
const struct key_entry *key;
int error;
status = acpi_get_handle(NULL, device, &handle);
status = acpi_get_handle(NULL, device, &toshiba_acpi.handle);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to get notification device\n");
return -ENODEV;
}
toshiba_acpi.handle = handle;
status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to enable hotkeys\n");
return -ENODEV;
}
status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY,
toshiba_acpi_notify, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to install hotkey notification\n");
return -ENODEV;
}
toshiba_acpi.hotkey_dev = input_allocate_device();
if (!toshiba_acpi.hotkey_dev) {
printk(MY_INFO "Unable to register input device\n");
@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device)
toshiba_acpi.hotkey_dev->name = "Toshiba input device";
toshiba_acpi.hotkey_dev->phys = device;
toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
error = sparse_keymap_setup(toshiba_acpi.hotkey_dev,
toshiba_acpi_keymap, NULL);
if (error)
goto err_free_dev;
status = acpi_install_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to install hotkey notification\n");
error = -ENODEV;
goto err_free_keymap;
}
result = input_register_device(toshiba_acpi.hotkey_dev);
if (result) {
status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(MY_INFO "Unable to enable hotkeys\n");
error = -ENODEV;
goto err_remove_notify;
}
error = input_register_device(toshiba_acpi.hotkey_dev);
if (error) {
printk(MY_INFO "Unable to register input device\n");
return result;
goto err_remove_notify;
}
return 0;
err_remove_notify:
acpi_remove_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
err_free_keymap:
sparse_keymap_free(toshiba_acpi.hotkey_dev);
err_free_dev:
input_free_device(toshiba_acpi.hotkey_dev);
toshiba_acpi.hotkey_dev = NULL;
return error;
}
static void toshiba_acpi_exit(void)
{
if (toshiba_acpi.hotkey_dev)
if (toshiba_acpi.hotkey_dev) {
acpi_remove_notify_handler(toshiba_acpi.handle,
ACPI_DEVICE_NOTIFY, toshiba_acpi_notify);
sparse_keymap_free(toshiba_acpi.hotkey_dev);
input_unregister_device(toshiba_acpi.hotkey_dev);
}
if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk);
@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void)
if (toshiba_proc_dir)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
toshiba_acpi_notify);
if (toshiba_acpi.illumination_installed)
led_classdev_unregister(&toshiba_led);

View file

@ -27,6 +27,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@ -44,9 +46,8 @@ MODULE_LICENSE("GPL");
#define ACPI_WMI_CLASS "wmi"
#define PREFIX "ACPI: WMI: "
static DEFINE_MUTEX(wmi_data_lock);
static LIST_HEAD(wmi_block_list);
struct guid_block {
char guid[16];
@ -67,10 +68,9 @@ struct wmi_block {
acpi_handle handle;
wmi_notify_handler handler;
void *handler_data;
struct device *dev;
struct device dev;
};
static struct wmi_block wmi_blocks;
/*
* If the GUID data block is marked as expensive, we must enable and
@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = {
*/
static int wmi_parse_hexbyte(const u8 *src)
{
unsigned int x; /* For correct wrapping */
int h;
int value;
/* high part */
x = src[0];
if (x - '0' <= '9' - '0') {
h = x - '0';
} else if (x - 'a' <= 'f' - 'a') {
h = x - 'a' + 10;
} else if (x - 'A' <= 'F' - 'A') {
h = x - 'A' + 10;
} else {
h = value = hex_to_bin(src[0]);
if (value < 0)
return -1;
}
h <<= 4;
/* low part */
x = src[1];
if (x - '0' <= '9' - '0')
return h | (x - '0');
if (x - 'a' <= 'f' - 'a')
return h | (x - 'a' + 10);
if (x - 'A' <= 'F' - 'A')
return h | (x - 'A' + 10);
value = hex_to_bin(src[1]);
if (value >= 0)
return (h << 4) | value;
return -1;
}
@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out)
for (i = 10; i <= 15; i++)
out += sprintf(out, "%02X", in[i] & 0xFF);
out = '\0';
*out = '\0';
return 0;
}
@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
wmi_parse_guid(guid_string, tmp);
wmi_swap_bytes(tmp, guid_input);
list_for_each(p, &wmi_blocks.list) {
list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
@ -487,30 +475,29 @@ const struct acpi_buffer *in)
}
EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(struct guid_block *g)
static void wmi_dump_wdg(const 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);
pr_info("%s:\n", guid_string);
pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
pr_info("\tnotify_id: %02X\n", g->notify_id);
pr_info("\treserved: %02X\n", g->reserved);
pr_info("\tinstance_count: %d\n", g->instance_count);
pr_info("\tflags: %#x ", g->flags);
if (g->flags) {
printk(" ");
if (g->flags & ACPI_WMI_EXPENSIVE)
printk("ACPI_WMI_EXPENSIVE ");
pr_cont("ACPI_WMI_EXPENSIVE ");
if (g->flags & ACPI_WMI_METHOD)
printk("ACPI_WMI_METHOD ");
pr_cont("ACPI_WMI_METHOD ");
if (g->flags & ACPI_WMI_STRING)
printk("ACPI_WMI_STRING ");
pr_cont("ACPI_WMI_STRING ");
if (g->flags & ACPI_WMI_EVENT)
printk("ACPI_WMI_EVENT ");
pr_cont("ACPI_WMI_EVENT ");
}
printk("\n");
pr_cont("\n");
}
@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context)
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
printk(KERN_INFO "wmi: bad event status 0x%x\n", status);
pr_info("bad event status 0x%x\n", status);
return;
}
@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context)
if (!obj)
return;
printk(KERN_INFO PREFIX "DEBUG Event ");
pr_info("DEBUG Event ");
switch(obj->type) {
case ACPI_TYPE_BUFFER:
printk("BUFFER_TYPE - length %d\n", obj->buffer.length);
pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length);
break;
case ACPI_TYPE_STRING:
printk("STRING_TYPE - %s\n", obj->string.pointer);
pr_cont("STRING_TYPE - %s\n", obj->string.pointer);
break;
case ACPI_TYPE_INTEGER:
printk("INTEGER_TYPE - %llu\n", obj->integer.value);
pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value);
break;
case ACPI_TYPE_PACKAGE:
printk("PACKAGE_TYPE - %d elements\n", obj->package.count);
pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count);
break;
default:
printk("object type 0x%X\n", obj->type);
pr_cont("object type 0x%X\n", obj->type);
}
kfree(obj);
}
@ -633,7 +620,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event;
list_for_each(p, &wmi_blocks.list) {
list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock;
@ -662,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
/*
* sysfs interface
*/
static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
char guid_string[37];
@ -676,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "wmi:%s\n", guid_string);
}
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
static struct device_attribute wmi_dev_attrs[] = {
__ATTR_RO(modalias),
__ATTR_NULL
};
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
@ -702,108 +693,71 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
static void wmi_dev_free(struct device *dev)
{
kfree(dev);
struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
kfree(wmi_block);
}
static struct class wmi_class = {
.name = "wmi",
.dev_release = wmi_dev_free,
.dev_uevent = wmi_dev_uevent,
.dev_attrs = wmi_dev_attrs,
};
static int wmi_create_devs(void)
static struct wmi_block *wmi_create_device(const struct guid_block *gblock,
acpi_handle handle)
{
int result;
char guid_string[37];
struct guid_block *gblock;
struct wmi_block *wblock;
struct list_head *p;
struct device *guid_dev;
int error;
char guid_string[37];
/* Create devices for all the GUIDs */
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock) {
error = -ENOMEM;
goto err_out;
}
guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!guid_dev)
return -ENOMEM;
wblock->handle = handle;
wblock->gblock = *gblock;
wblock->dev = guid_dev;
guid_dev->class = &wmi_class;
dev_set_drvdata(guid_dev, wblock);
gblock = &wblock->gblock;
wblock->dev.class = &wmi_class;
wmi_gtoa(gblock->guid, guid_string);
dev_set_name(guid_dev, guid_string);
dev_set_name(&wblock->dev, guid_string);
result = device_register(guid_dev);
if (result)
return result;
dev_set_drvdata(&wblock->dev, wblock);
result = device_create_file(guid_dev, &dev_attr_modalias);
if (result)
return result;
error = device_register(&wblock->dev);
if (error)
goto err_free;
list_add_tail(&wblock->list, &wmi_block_list);
return wblock;
err_free:
kfree(wblock);
err_out:
return ERR_PTR(error);
}
return 0;
}
static void wmi_remove_devs(void)
static void wmi_free_devices(void)
{
struct guid_block *gblock;
struct wmi_block *wblock;
struct list_head *p;
struct device *guid_dev;
struct wmi_block *wblock, *next;
/* Delete devices for all the GUIDs */
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
guid_dev = wblock->dev;
gblock = &wblock->gblock;
device_remove_file(guid_dev, &dev_attr_modalias);
device_unregister(guid_dev);
}
}
static void wmi_class_exit(void)
{
wmi_remove_devs();
class_unregister(&wmi_class);
}
static int wmi_class_init(void)
{
int ret;
ret = class_register(&wmi_class);
if (ret)
return ret;
ret = wmi_create_devs();
if (ret)
wmi_class_exit();
return ret;
list_for_each_entry_safe(wblock, next, &wmi_block_list, list)
device_unregister(&wblock->dev);
}
static bool guid_already_parsed(const char *guid_string)
{
struct guid_block *gblock;
struct wmi_block *wblock;
struct list_head *p;
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock;
if (strncmp(gblock->guid, guid_string, 16) == 0)
list_for_each_entry(wblock, &wmi_block_list, list)
if (strncmp(wblock->gblock.guid, guid_string, 16) == 0)
return true;
}
return false;
}
@ -814,30 +768,29 @@ static acpi_status parse_wdg(acpi_handle handle)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
struct guid_block *gblock;
const struct guid_block *gblock;
struct wmi_block *wblock;
char guid_string[37];
acpi_status status;
int retval;
u32 i, total;
status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
if (ACPI_FAILURE(status))
return status;
return -ENXIO;
obj = (union acpi_object *) out.pointer;
if (!obj)
return -ENXIO;
if (obj->type != ACPI_TYPE_BUFFER)
return AE_ERROR;
total = obj->buffer.length / sizeof(struct guid_block);
gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL);
if (!gblock) {
status = AE_NO_MEMORY;
if (obj->type != ACPI_TYPE_BUFFER) {
retval = -ENXIO;
goto out_free_pointer;
}
gblock = (const struct guid_block *)obj->buffer.pointer;
total = obj->buffer.length / sizeof(struct guid_block);
for (i = 0; i < total; i++) {
/*
Some WMI devices, like those for nVidia hooks, have a
@ -848,34 +801,32 @@ static acpi_status parse_wdg(acpi_handle handle)
*/
if (guid_already_parsed(gblock[i].guid) == true) {
wmi_gtoa(gblock[i].guid, guid_string);
printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
guid_string);
pr_info("Skipping duplicate GUID %s\n", guid_string);
continue;
}
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock) {
status = AE_NO_MEMORY;
goto out_free_gblock;
wblock = wmi_create_device(&gblock[i], handle);
if (IS_ERR(wblock)) {
retval = PTR_ERR(wblock);
wmi_free_devices();
break;
}
wblock->gblock = gblock[i];
wblock->handle = handle;
if (debug_event) {
wblock->handler = wmi_notify_debug;
status = wmi_method_enable(wblock, 1);
wmi_method_enable(wblock, 1);
}
list_add_tail(&wblock->list, &wmi_blocks.list);
}
out_free_gblock:
kfree(gblock);
retval = 0;
out_free_pointer:
kfree(out.pointer);
return status;
return retval;
}
/*
@ -929,7 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
struct list_head *p;
char guid_string[37];
list_for_each(p, &wmi_blocks.list) {
list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
@ -939,8 +890,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
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);
pr_info("DEBUG Event GUID: %s\n", guid_string);
}
acpi_bus_generate_netlink_event(
@ -955,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
{
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
wmi_free_devices();
return 0;
}
@ -962,68 +913,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type)
static int acpi_wmi_add(struct acpi_device *device)
{
acpi_status status;
int result = 0;
int error;
status = acpi_install_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler,
NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
status = parse_wdg(device->handle);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Error installing EC region handler\n");
pr_err("Error installing EC region handler\n");
return -ENODEV;
}
return result;
error = parse_wdg(device->handle);
if (error) {
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler);
pr_err("Failed to parse WDG method\n");
return error;
}
return 0;
}
static int __init acpi_wmi_init(void)
{
int result;
INIT_LIST_HEAD(&wmi_blocks.list);
int error;
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&acpi_wmi_driver);
error = class_register(&wmi_class);
if (error)
return error;
if (result < 0) {
printk(KERN_INFO PREFIX "Error loading mapper\n");
return -ENODEV;
error = acpi_bus_register_driver(&acpi_wmi_driver);
if (error) {
pr_err("Error loading mapper\n");
class_unregister(&wmi_class);
return error;
}
result = wmi_class_init();
if (result) {
acpi_bus_unregister_driver(&acpi_wmi_driver);
return result;
}
printk(KERN_INFO PREFIX "Mapper loaded\n");
return result;
pr_info("Mapper loaded\n");
return 0;
}
static void __exit acpi_wmi_exit(void)
{
struct list_head *p, *tmp;
struct wmi_block *wblock;
wmi_class_exit();
acpi_bus_unregister_driver(&acpi_wmi_driver);
class_unregister(&wmi_class);
list_for_each_safe(p, tmp, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
list_del(p);
kfree(wblock);
}
printk(KERN_INFO PREFIX "Mapper unloaded\n");
pr_info("Mapper unloaded\n");
}
subsys_initcall(acpi_wmi_init);

View file

@ -0,0 +1,85 @@
/*
* Support for rfkill through the OLPC XO-1 laptop embedded controller
*
* Copyright (C) 2010 One Laptop per Child
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <asm/olpc.h>
static int rfkill_set_block(void *data, bool blocked)
{
unsigned char cmd;
if (blocked)
cmd = EC_WLAN_ENTER_RESET;
else
cmd = EC_WLAN_LEAVE_RESET;
return olpc_ec_cmd(cmd, NULL, 0, NULL, 0);
}
static const struct rfkill_ops rfkill_ops = {
.set_block = rfkill_set_block,
};
static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
{
struct rfkill *rfk;
int r;
rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN,
&rfkill_ops, NULL);
if (!rfk)
return -ENOMEM;
r = rfkill_register(rfk);
if (r) {
rfkill_destroy(rfk);
return r;
}
platform_set_drvdata(pdev, rfk);
return 0;
}
static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
{
struct rfkill *rfk = platform_get_drvdata(pdev);
rfkill_unregister(rfk);
rfkill_destroy(rfk);
return 0;
}
static struct platform_driver xo1_rfkill_driver = {
.driver = {
.name = "xo1-rfkill",
.owner = THIS_MODULE,
},
.probe = xo1_rfkill_probe,
.remove = __devexit_p(xo1_rfkill_remove),
};
static int __init xo1_rfkill_init(void)
{
return platform_driver_register(&xo1_rfkill_driver);
}
static void __exit xo1_rfkill_exit(void)
{
platform_driver_unregister(&xo1_rfkill_driver);
}
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:xo1-rfkill");
module_init(xo1_rfkill_init);
module_exit(xo1_rfkill_exit);