create drivers/platform/x86/ from drivers/misc/

Move x86 platform specific drivers from drivers/misc/
to a new home under drivers/platform/x86/.

The community has been maintaining x86 vendor-specific
platform specific drivers under /drivers/misc/ for a few years.
The oldest ones started life under drivers/acpi.
They moved out of drivers/acpi/ because they don't actually
implement the ACPI specification, but either simply
use ACPI, or implement vendor-specific ACPI extensions.

In the future we anticipate...
drivers/misc/ will go away.
other architectures will create drivers/platform/<arch>

Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
Len Brown
2008-12-01 00:09:47 -05:00
parent 8c36790a21
commit 41b16dce39
20 changed files with 319 additions and 292 deletions

5
drivers/platform/Kconfig Normal file
View File

@@ -0,0 +1,5 @@
# drivers/platform/Kconfig
if X86
source "drivers/platform/x86/Kconfig"
endif

View File

@@ -0,0 +1,5 @@
#
# Makefile for linux/drivers/platform
#
obj-$(CONFIG_X86) += x86/

View File

@@ -0,0 +1,290 @@
#
# X86 Platform Specific Drivers
#
menuconfig X86_PLATFORM_DEVICES
bool "X86 Platform Specific Device Drivers"
default y
---help---
Say Y here to get to see options for device drivers for various
x86 platforms, including vendor-specific laptop extension drivers.
This option alone does not add any kernel code.
If you say N, all options in this submenu will be skipped and disabled.
if X86_PLATFORM_DEVICES
config ACER_WMI
tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on ACPI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
depends on RFKILL
select ACPI_WMI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops,
exposes the mail LED and LCD backlight.
For more information about this driver see
<file:Documentation/laptops/acer-wmi.txt>
If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
here.
config ASUS_LAPTOP
tristate "Asus Laptop Extras (EXPERIMENTAL)"
depends on ACPI
depends on EXPERIMENTAL && !ACPI_ASUS
depends on LEDS_CLASS
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is the new Linux driver for Asus laptops. It may also support some
MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
standard ACPI events that go through /proc/acpi/events. It also adds
support for video output switching, LCD backlight control, Bluetooth and
Wlan control, and most importantly, allows you to blink those fancy LEDs.
For more information and a userspace daemon for handling the extra
buttons see <http://acpi4asus.sf.net/>.
If you have an ACPI-compatible ASUS laptop, say Y or M here.
config FUJITSU_LAPTOP
tristate "Fujitsu Laptop Extras"
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is a driver for laptops built by Fujitsu:
* P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks
* Possibly other Fujitsu laptop models
* Tested with S6410 and S7020
It adds support for LCD brightness control and some hotkeys.
If you have a Fujitsu laptop, say Y or M here.
config FUJITSU_LAPTOP_DEBUG
bool "Verbose debug mode for Fujitsu Laptop Extras"
depends on FUJITSU_LAPTOP
default n
---help---
Enables extra debug output from the fujitsu extras driver, at the
expense of a slight increase in driver size.
If you are not sure, say N here.
config TC1100_WMI
tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
depends on !X86_64
depends on EXPERIMENTAL
depends on ACPI
select ACPI_WMI
---help---
This is a driver for the WMI extensions (wireless and bluetooth power
control) of the HP Compaq TC1100 tablet.
config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
depends on INPUT
depends on RFKILL
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.
To compile this driver as a module, choose M here: the module will
be called hp-wmi.
config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is a driver for laptops built by MSI (MICRO-STAR
INTERNATIONAL):
MSI MegaBook S270 (MS-1013)
Cytron/TCM/Medion/Tchibo MD96100/SAM2000
It adds support for Bluetooth, WLAN and LCD brightness control.
More information about this driver is available at
<http://0pointer.de/lennart/tchibo.html>.
If you have an MSI S270 laptop, say Y or M here.
config PANASONIC_LAPTOP
tristate "Panasonic Laptop Extras"
depends on INPUT && ACPI
depends on BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to backlight control and hotkeys
on Panasonic Let's Note laptops.
If you have a Panasonic Let's note laptop (such as the R1(N variant),
R2, R3, R5, T2, W2 and Y2 series), say Y.
config COMPAL_LAPTOP
tristate "Compal Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is a driver for laptops built by Compal:
Compal FL90/IFL90
Compal FL91/IFL91
Compal FL92/JFL92
Compal FT00/IFT00
It adds support for Bluetooth, WLAN and LCD brightness control.
If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on ACPI
select BACKLIGHT_CLASS_DEVICE
depends on INPUT
---help---
This mini-driver drives the SNC and SPIC devices present in the ACPI
BIOS of the Sony Vaio laptops.
It gives access to some extra laptop functionalities like Bluetooth,
screen brightness control, Fn keys and allows powering on/off some
devices.
Read <file:Documentation/laptops/sony-laptop.txt> for more information.
config SONYPI_COMPAT
bool "Sonypi compatibility"
depends on SONY_LAPTOP
---help---
Build the sonypi driver compatibility code into the sony-laptop driver.
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
depends on ACPI
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
select INPUT
select NEW_LEDS
select LEDS_CLASS
select NET
select RFKILL
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
output switching, ThinkLight control, UltraBay eject and more.
For more information about this driver see
<file:Documentation/laptops/thinkpad-acpi.txt> and
<http://ibm-acpi.sf.net/> .
This driver was formerly known as ibm-acpi.
If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
config THINKPAD_ACPI_DEBUG
bool "Verbose debug mode"
depends on THINKPAD_ACPI
default n
---help---
Enables extra debugging information, at the expense of a slightly
increase in driver size.
If you are not sure, say N here.
config THINKPAD_ACPI_DOCK
bool "Legacy Docking Station Support"
depends on THINKPAD_ACPI
depends on ACPI_DOCK=n
default n
---help---
Allows the thinkpad_acpi driver to handle docking station events.
This support was made obsolete by the generic ACPI docking station
support (CONFIG_ACPI_DOCK). It will allow locking and removing the
laptop from the docking station, but will not properly connect PCI
devices.
If you are not sure, say N here.
config THINKPAD_ACPI_BAY
bool "Legacy Removable Bay Support"
depends on THINKPAD_ACPI
default y
---help---
Allows the thinkpad_acpi driver to handle removable bays. It will
electrically disable the device in the bay, and also generate
notifications when the bay lever is ejected or inserted.
If you are not sure, say Y here.
config THINKPAD_ACPI_VIDEO
bool "Video output control support"
depends on THINKPAD_ACPI
default y
---help---
Allows the thinkpad_acpi driver to provide an interface to control
the various video output ports.
This feature often won't work well, depending on ThinkPad model,
display state, video output devices in use, whether there is a X
server running, phase of the moon, and the current mood of
Schroedinger's cat. If you can use X.org's RandR to control
your ThinkPad's video output ports instead of this feature,
don't think twice: do it and say N here to save some memory.
If you are not sure, say Y here.
config THINKPAD_ACPI_HOTKEY_POLL
bool "Support NVRAM polling for hot keys"
depends on THINKPAD_ACPI
default y
---help---
Some thinkpad models benefit from NVRAM polling to detect a few of
the hot key press events. If you know your ThinkPad model does not
need to do NVRAM polling to support any of the hot keys you use,
unselecting this option will save about 1kB of memory.
ThinkPads T40 and newer, R52 and newer, and X31 and newer are
unlikely to need NVRAM polling in their latest BIOS versions.
NVRAM polling can detect at most the following keys: ThinkPad/Access
IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
If you are not sure, say Y here. The driver enables polling only if
it is strictly necessary to do so.
config INTEL_MENLOW
tristate "Thermal Management driver for Intel menlow platform"
depends on ACPI_THERMAL
select THERMAL
---help---
ACPI thermal management enhancement driver on
Intel Menlow platform.
If unsure, say N.
config EEEPC_LAPTOP
tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
depends on HWMON
depends on EXPERIMENTAL
depends on RFKILL
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off.
If you have an Eee PC laptop, say Y or M here.
endif # X86_PLATFORM_DEVICES

View File

@@ -0,0 +1,16 @@
#
# Makefile for linux/drivers/platform/x86
# x86 Platform-Specific Drivers
#
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,406 @@
/*-*-linux-c-*-*/
/*
Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
based on MSI driver
Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
/*
* comapl-laptop.c - Compal laptop support.
*
* This driver exports a few files in /sys/devices/platform/compal-laptop/:
*
* wlan - wlan subsystem state: contains 0 or 1 (rw)
*
* bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
*
* raw - raw value taken from embedded controller register (ro)
*
* In addition to these platform device attributes the driver
* registers itself in the Linux backlight control subsystem and is
* available to userspace under /sys/class/backlight/compal-laptop/.
*
* This driver might work on other laptops produced by Compal. If you
* want to try it you can pass force=1 as argument to the module which
* will force it to load even when the DMI data doesn't identify the
* laptop as FL9x.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/autoconf.h>
#define COMPAL_DRIVER_VERSION "0.2.6"
#define COMPAL_LCD_LEVEL_MAX 8
#define COMPAL_EC_COMMAND_WIRELESS 0xBB
#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
#define KILLSWITCH_MASK 0x10
#define WLAN_MASK 0x01
#define BT_MASK 0x02
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
/* Hardware access */
static int set_lcd_level(int level)
{
if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
return -EINVAL;
ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
return 0;
}
static int get_lcd_level(void)
{
u8 result;
ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
return (int) result;
}
static int set_wlan_state(int state)
{
u8 result, value;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
if ((result & KILLSWITCH_MASK) == 0)
return -EINVAL;
else {
if (state)
value = (u8) (result | WLAN_MASK);
else
value = (u8) (result & ~WLAN_MASK);
ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
}
return 0;
}
static int set_bluetooth_state(int state)
{
u8 result, value;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
if ((result & KILLSWITCH_MASK) == 0)
return -EINVAL;
else {
if (state)
value = (u8) (result | BT_MASK);
else
value = (u8) (result & ~BT_MASK);
ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
}
return 0;
}
static int get_wireless_state(int *wlan, int *bluetooth)
{
u8 result;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
if (wlan) {
if ((result & KILLSWITCH_MASK) == 0)
*wlan = 0;
else
*wlan = result & WLAN_MASK;
}
if (bluetooth) {
if ((result & KILLSWITCH_MASK) == 0)
*bluetooth = 0;
else
*bluetooth = (result & BT_MASK) >> 1;
}
return 0;
}
/* Backlight device stuff */
static int bl_get_brightness(struct backlight_device *b)
{
return get_lcd_level();
}
static int bl_update_status(struct backlight_device *b)
{
return set_lcd_level(b->props.brightness);
}
static struct backlight_ops compalbl_ops = {
.get_brightness = bl_get_brightness,
.update_status = bl_update_status,
};
static struct backlight_device *compalbl_device;
/* Platform device */
static ssize_t show_wlan(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(&enabled, NULL);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t show_raw(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 result;
ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
return sprintf(buf, "%i\n", result);
}
static ssize_t show_bluetooth(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(NULL, &enabled);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t store_wlan_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int state, ret;
if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
return -EINVAL;
ret = set_wlan_state(state);
if (ret < 0)
return ret;
return count;
}
static ssize_t store_bluetooth_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int state, ret;
if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
return -EINVAL;
ret = set_bluetooth_state(state);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
static DEVICE_ATTR(raw, 0444, show_raw, NULL);
static struct attribute *compal_attributes[] = {
&dev_attr_bluetooth.attr,
&dev_attr_wlan.attr,
&dev_attr_raw.attr,
NULL
};
static struct attribute_group compal_attribute_group = {
.attrs = compal_attributes
};
static struct platform_driver compal_driver = {
.driver = {
.name = "compal-laptop",
.owner = THIS_MODULE,
}
};
static struct platform_device *compal_device;
/* Initialization */
static int dmi_check_cb(const struct dmi_system_id *id)
{
printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
id->ident);
return 0;
}
static struct dmi_system_id __initdata compal_dmi_table[] = {
{
.ident = "FL90/IFL90",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
},
.callback = dmi_check_cb
},
{
.ident = "FL90/IFL90",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
},
.callback = dmi_check_cb
},
{
.ident = "FL91/IFL91",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
},
.callback = dmi_check_cb
},
{
.ident = "FL92/JFL92",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
},
.callback = dmi_check_cb
},
{
.ident = "FT00/IFT00",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
},
.callback = dmi_check_cb
},
{ }
};
static int __init compal_init(void)
{
int ret;
if (acpi_disabled)
return -ENODEV;
if (!force && !dmi_check_system(compal_dmi_table))
return -ENODEV;
/* Register backlight stuff */
if (!acpi_video_backlight_support()) {
compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
&compalbl_ops);
if (IS_ERR(compalbl_device))
return PTR_ERR(compalbl_device);
compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
}
ret = platform_driver_register(&compal_driver);
if (ret)
goto fail_backlight;
/* Register platform stuff */
compal_device = platform_device_alloc("compal-laptop", -1);
if (!compal_device) {
ret = -ENOMEM;
goto fail_platform_driver;
}
ret = platform_device_add(compal_device);
if (ret)
goto fail_platform_device1;
ret = sysfs_create_group(&compal_device->dev.kobj,
&compal_attribute_group);
if (ret)
goto fail_platform_device2;
printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
" successfully loaded.\n");
return 0;
fail_platform_device2:
platform_device_del(compal_device);
fail_platform_device1:
platform_device_put(compal_device);
fail_platform_driver:
platform_driver_unregister(&compal_driver);
fail_backlight:
backlight_device_unregister(compalbl_device);
return ret;
}
static void __exit compal_cleanup(void)
{
sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
platform_device_unregister(compal_device);
platform_driver_unregister(&compal_driver);
backlight_device_unregister(compalbl_device);
printk(KERN_INFO "compal-laptop: driver unloaded.\n");
}
module_init(compal_init);
module_exit(compal_cleanup);
MODULE_AUTHOR("Cezary Jackiewicz");
MODULE_DESCRIPTION("Compal Laptop Support");
MODULE_VERSION(COMPAL_DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");

View File

@@ -0,0 +1,872 @@
/*
* eepc-laptop.c - Asus Eee PC extras
*
* Based on asus_acpi.c as patched for the Eee PC by Asus:
* ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
* Based on eee.c from eeepc-linux
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/rfkill.h>
#define EEEPC_LAPTOP_VERSION "0.1"
#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
#define EEEPC_HOTK_FILE "eeepc"
#define EEEPC_HOTK_CLASS "hotkey"
#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
#define EEEPC_HOTK_HID "ASUS010"
#define EEEPC_LOG EEEPC_HOTK_FILE ": "
#define EEEPC_ERR KERN_ERR EEEPC_LOG
#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
#define EEEPC_INFO KERN_INFO EEEPC_LOG
/*
* Definitions for Asus EeePC
*/
#define NOTIFY_WLAN_ON 0x10
#define NOTIFY_BRN_MIN 0x20
#define NOTIFY_BRN_MAX 0x2f
enum {
DISABLE_ASL_WLAN = 0x0001,
DISABLE_ASL_BLUETOOTH = 0x0002,
DISABLE_ASL_IRDA = 0x0004,
DISABLE_ASL_CAMERA = 0x0008,
DISABLE_ASL_TV = 0x0010,
DISABLE_ASL_GPS = 0x0020,
DISABLE_ASL_DISPLAYSWITCH = 0x0040,
DISABLE_ASL_MODEM = 0x0080,
DISABLE_ASL_CARDREADER = 0x0100
};
enum {
CM_ASL_WLAN = 0,
CM_ASL_BLUETOOTH,
CM_ASL_IRDA,
CM_ASL_1394,
CM_ASL_CAMERA,
CM_ASL_TV,
CM_ASL_GPS,
CM_ASL_DVDROM,
CM_ASL_DISPLAYSWITCH,
CM_ASL_PANELBRIGHT,
CM_ASL_BIOSFLASH,
CM_ASL_ACPIFLASH,
CM_ASL_CPUFV,
CM_ASL_CPUTEMPERATURE,
CM_ASL_FANCPU,
CM_ASL_FANCHASSIS,
CM_ASL_USBPORT1,
CM_ASL_USBPORT2,
CM_ASL_USBPORT3,
CM_ASL_MODEM,
CM_ASL_CARDREADER,
CM_ASL_LID
};
static const char *cm_getv[] = {
"WLDG", NULL, NULL, NULL,
"CAMG", NULL, NULL, NULL,
NULL, "PBLG", NULL, NULL,
"CFVG", NULL, NULL, NULL,
"USBG", NULL, NULL, "MODG",
"CRDG", "LIDG"
};
static const char *cm_setv[] = {
"WLDS", NULL, NULL, NULL,
"CAMS", NULL, NULL, NULL,
"SDSP", "PBLS", "HDPS", NULL,
"CFVS", NULL, NULL, NULL,
"USBG", NULL, NULL, "MODS",
"CRDS", NULL
};
#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
#define EEEPC_EC_SC02 0x63
#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
#define EEEPC_EC_SFB3 0xD3
/*
* This is the main structure, we can use it to store useful information
* about the hotk device
*/
struct eeepc_hotk {
struct acpi_device *device; /* the device we are in */
acpi_handle handle; /* the handle of the hotk device */
u32 cm_supported; /* the control methods supported
by this BIOS */
uint init_flag; /* Init flags */
u16 event_count[128]; /* count for each event */
struct input_dev *inputdev;
u16 *keycode_map;
struct rfkill *eeepc_wlan_rfkill;
struct rfkill *eeepc_bluetooth_rfkill;
};
/* The actual device the driver binds to */
static struct eeepc_hotk *ehotk;
/* Platform device/driver */
static struct platform_driver platform_driver = {
.driver = {
.name = EEEPC_HOTK_FILE,
.owner = THIS_MODULE,
}
};
static struct platform_device *platform_device;
struct key_entry {
char type;
u8 code;
u16 keycode;
};
enum { KE_KEY, KE_END };
static struct key_entry eeepc_keymap[] = {
/* Sleep already handled via generic ACPI code */
{KE_KEY, 0x10, KEY_WLAN },
{KE_KEY, 0x12, KEY_PROG1 },
{KE_KEY, 0x13, KEY_MUTE },
{KE_KEY, 0x14, KEY_VOLUMEDOWN },
{KE_KEY, 0x15, KEY_VOLUMEUP },
{KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
{KE_END, 0},
};
/*
* The hotkey driver declaration
*/
static int eeepc_hotk_add(struct acpi_device *device);
static int eeepc_hotk_remove(struct acpi_device *device, int type);
static const struct acpi_device_id eeepc_device_ids[] = {
{EEEPC_HOTK_HID, 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
static struct acpi_driver eeepc_hotk_driver = {
.name = EEEPC_HOTK_NAME,
.class = EEEPC_HOTK_CLASS,
.ids = eeepc_device_ids,
.ops = {
.add = eeepc_hotk_add,
.remove = eeepc_hotk_remove,
},
};
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;
/* The hwmon device */
static struct device *eeepc_hwmon_device;
/*
* The backlight class declaration
*/
static int read_brightness(struct backlight_device *bd);
static int update_bl_status(struct backlight_device *bd);
static struct backlight_ops eeepcbl_ops = {
.get_brightness = read_brightness,
.update_status = update_bl_status,
};
MODULE_AUTHOR("Corentin Chary, Eric Cooper");
MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
MODULE_LICENSE("GPL");
/*
* ACPI Helpers
*/
static int write_acpi_int(acpi_handle handle, const char *method, int val,
struct acpi_buffer *output)
{
struct acpi_object_list params;
union acpi_object in_obj;
acpi_status status;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = val;
status = acpi_evaluate_object(handle, (char *)method, &params, output);
return (status == AE_OK ? 0 : -1);
}
static int read_acpi_int(acpi_handle handle, const char *method, int *val)
{
acpi_status status;
unsigned long long result;
status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
if (ACPI_FAILURE(status)) {
*val = -1;
return -1;
} else {
*val = result;
return 0;
}
}
static int set_acpi(int cm, int value)
{
if (ehotk->cm_supported & (0x1 << cm)) {
const char *method = cm_setv[cm];
if (method == NULL)
return -ENODEV;
if (write_acpi_int(ehotk->handle, method, value, NULL))
printk(EEEPC_WARNING "Error writing %s\n", method);
}
return 0;
}
static int get_acpi(int cm)
{
int value = -1;
if ((ehotk->cm_supported & (0x1 << cm))) {
const char *method = cm_getv[cm];
if (method == NULL)
return -ENODEV;
if (read_acpi_int(ehotk->handle, method, &value))
printk(EEEPC_WARNING "Error reading %s\n", method);
}
return value;
}
/*
* Backlight
*/
static int read_brightness(struct backlight_device *bd)
{
return get_acpi(CM_ASL_PANELBRIGHT);
}
static int set_brightness(struct backlight_device *bd, int value)
{
value = max(0, min(15, value));
return set_acpi(CM_ASL_PANELBRIGHT, value);
}
static int update_bl_status(struct backlight_device *bd)
{
return set_brightness(bd, bd->props.brightness);
}
/*
* Rfkill helpers
*/
static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
{
if (state == RFKILL_STATE_SOFT_BLOCKED)
return set_acpi(CM_ASL_WLAN, 0);
else
return set_acpi(CM_ASL_WLAN, 1);
}
static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
{
if (get_acpi(CM_ASL_WLAN) == 1)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
}
static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
{
if (state == RFKILL_STATE_SOFT_BLOCKED)
return set_acpi(CM_ASL_BLUETOOTH, 0);
else
return set_acpi(CM_ASL_BLUETOOTH, 1);
}
static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
{
if (get_acpi(CM_ASL_BLUETOOTH) == 1)
*state = RFKILL_STATE_UNBLOCKED;
else
*state = RFKILL_STATE_SOFT_BLOCKED;
return 0;
}
/*
* Sys helpers
*/
static int parse_arg(const char *buf, unsigned long count, int *val)
{
if (!count)
return 0;
if (sscanf(buf, "%i", val) != 1)
return -EINVAL;
return count;
}
static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
{
int rv, value;
rv = parse_arg(buf, count, &value);
if (rv > 0)
set_acpi(cm, value);
return rv;
}
static ssize_t show_sys_acpi(int cm, char *buf)
{
return sprintf(buf, "%d\n", get_acpi(cm));
}
#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
static ssize_t show_##_name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_sys_acpi(_cm, buf); \
} \
static ssize_t store_##_name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return store_sys_acpi(_cm, buf, count); \
} \
static struct device_attribute dev_attr_##_name = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644 }, \
.show = show_##_name, \
.store = store_##_name, \
}
EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_disp.attr,
NULL
};
static struct attribute_group platform_attribute_group = {
.attrs = platform_attributes
};
/*
* Hotkey functions
*/
static struct key_entry *eepc_get_entry_by_scancode(int code)
{
struct key_entry *key;
for (key = eeepc_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *eepc_get_entry_by_keycode(int code)
{
struct key_entry *key;
for (key = eeepc_keymap; key->type != KE_END; key++)
if (code == key->keycode && key->type == KE_KEY)
return key;
return NULL;
}
static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
{
struct key_entry *key = eepc_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
struct key_entry *key;
int old_keycode;
if (keycode < 0 || keycode > KEY_MAX)
return -EINVAL;
key = eepc_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!eepc_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static int eeepc_hotk_check(void)
{
const struct key_entry *key;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
int result;
result = acpi_bus_get_status(ehotk->device);
if (result)
return result;
if (ehotk->device->status.present) {
if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
&buffer)) {
printk(EEEPC_ERR "Hotkey initialization failed\n");
return -ENODEV;
} else {
printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
ehotk->init_flag);
}
/* get control methods supported */
if (read_acpi_int(ehotk->handle, "CMSG"
, &ehotk->cm_supported)) {
printk(EEEPC_ERR
"Get control methods supported failed\n");
return -ENODEV;
} else {
printk(EEEPC_INFO
"Get control methods supported: 0x%x\n",
ehotk->cm_supported);
}
ehotk->inputdev = input_allocate_device();
if (!ehotk->inputdev) {
printk(EEEPC_INFO "Unable to allocate input device\n");
return 0;
}
ehotk->inputdev->name = "Asus EeePC extra buttons";
ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
ehotk->inputdev->id.bustype = BUS_HOST;
ehotk->inputdev->getkeycode = eeepc_getkeycode;
ehotk->inputdev->setkeycode = eeepc_setkeycode;
for (key = eeepc_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, ehotk->inputdev->evbit);
set_bit(key->keycode, ehotk->inputdev->keybit);
break;
}
}
result = input_register_device(ehotk->inputdev);
if (result) {
printk(EEEPC_INFO "Unable to register input device\n");
input_free_device(ehotk->inputdev);
return 0;
}
} else {
printk(EEEPC_ERR "Hotkey device not present, aborting\n");
return -EINVAL;
}
return 0;
}
static void notify_brn(void)
{
struct backlight_device *bd = eeepc_backlight_device;
bd->props.brightness = read_brightness(bd);
}
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
static struct key_entry *key;
if (!ehotk)
return;
if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
notify_brn();
acpi_bus_generate_proc_event(ehotk->device, event,
ehotk->event_count[event % 128]++);
if (ehotk->inputdev) {
key = eepc_get_entry_by_scancode(event);
if (key) {
switch (key->type) {
case KE_KEY:
input_report_key(ehotk->inputdev, key->keycode,
1);
input_sync(ehotk->inputdev);
input_report_key(ehotk->inputdev, key->keycode,
0);
input_sync(ehotk->inputdev);
break;
}
}
}
}
static int eeepc_hotk_add(struct acpi_device *device)
{
acpi_status status = AE_OK;
int result;
if (!device)
return -EINVAL;
printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
if (!ehotk)
return -ENOMEM;
ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
ehotk->handle = device->handle;
strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
device->driver_data = ehotk;
ehotk->device = device;
result = eeepc_hotk_check();
if (result)
goto end;
status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
eeepc_hotk_notify, ehotk);
if (ACPI_FAILURE(status))
printk(EEEPC_ERR "Error installing notify handler\n");
if (get_acpi(CM_ASL_WLAN) != -1) {
ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_WLAN);
if (!ehotk->eeepc_wlan_rfkill)
goto end;
ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
if (get_acpi(CM_ASL_WLAN) == 1)
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_UNBLOCKED;
else
ehotk->eeepc_wlan_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_register(ehotk->eeepc_wlan_rfkill);
}
if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
ehotk->eeepc_bluetooth_rfkill =
rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
if (!ehotk->eeepc_bluetooth_rfkill)
goto end;
ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
ehotk->eeepc_bluetooth_rfkill->toggle_radio =
eeepc_bluetooth_rfkill_set;
ehotk->eeepc_bluetooth_rfkill->get_state =
eeepc_bluetooth_rfkill_state;
if (get_acpi(CM_ASL_BLUETOOTH) == 1)
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_UNBLOCKED;
else
ehotk->eeepc_bluetooth_rfkill->state =
RFKILL_STATE_SOFT_BLOCKED;
rfkill_register(ehotk->eeepc_bluetooth_rfkill);
}
end:
if (result) {
kfree(ehotk);
ehotk = NULL;
}
return result;
}
static int eeepc_hotk_remove(struct acpi_device *device, int type)
{
acpi_status status = 0;
if (!device || !acpi_driver_data(device))
return -EINVAL;
status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
eeepc_hotk_notify);
if (ACPI_FAILURE(status))
printk(EEEPC_ERR "Error removing notify handler\n");
kfree(ehotk);
return 0;
}
/*
* Hwmon
*/
static int eeepc_get_fan_pwm(void)
{
int value = 0;
read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
value = value * 255 / 100;
return (value);
}
static void eeepc_set_fan_pwm(int value)
{
value = SENSORS_LIMIT(value, 0, 255);
value = value * 100 / 255;
ec_write(EEEPC_EC_SC02, value);
}
static int eeepc_get_fan_rpm(void)
{
int high = 0;
int low = 0;
read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
return (high << 8 | low);
}
static int eeepc_get_fan_ctrl(void)
{
int value = 0;
read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
return ((value & 0x02 ? 1 : 0));
}
static void eeepc_set_fan_ctrl(int manual)
{
int value = 0;
read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
if (manual)
value |= 0x02;
else
value &= ~0x02;
ec_write(EEEPC_EC_SFB3, value);
}
static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
{
int rv, value;
rv = parse_arg(buf, count, &value);
if (rv > 0)
set(value);
return rv;
}
static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
{
return sprintf(buf, "%d\n", get());
}
#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
static ssize_t show_##_name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_sys_hwmon(_set, buf); \
} \
static ssize_t store_##_name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return store_sys_hwmon(_get, buf, count); \
} \
static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
eeepc_get_fan_pwm, eeepc_set_fan_pwm);
EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "eeepc\n");
}
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_name.dev_attr.attr,
NULL
};
static struct attribute_group hwmon_attribute_group = {
.attrs = hwmon_attributes
};
/*
* exit/init
*/
static void eeepc_backlight_exit(void)
{
if (eeepc_backlight_device)
backlight_device_unregister(eeepc_backlight_device);
if (ehotk->inputdev)
input_unregister_device(ehotk->inputdev);
if (ehotk->eeepc_wlan_rfkill)
rfkill_unregister(ehotk->eeepc_wlan_rfkill);
if (ehotk->eeepc_bluetooth_rfkill)
rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
eeepc_backlight_device = NULL;
}
static void eeepc_hwmon_exit(void)
{
struct device *hwmon;
hwmon = eeepc_hwmon_device;
if (!hwmon)
return ;
sysfs_remove_group(&hwmon->kobj,
&hwmon_attribute_group);
hwmon_device_unregister(hwmon);
eeepc_hwmon_device = NULL;
}
static void __exit eeepc_laptop_exit(void)
{
eeepc_backlight_exit();
eeepc_hwmon_exit();
acpi_bus_unregister_driver(&eeepc_hotk_driver);
sysfs_remove_group(&platform_device->dev.kobj,
&platform_attribute_group);
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
static int eeepc_backlight_init(struct device *dev)
{
struct backlight_device *bd;
bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
NULL, &eeepcbl_ops);
if (IS_ERR(bd)) {
printk(EEEPC_ERR
"Could not register eeepc backlight device\n");
eeepc_backlight_device = NULL;
return PTR_ERR(bd);
}
eeepc_backlight_device = bd;
bd->props.max_brightness = 15;
bd->props.brightness = read_brightness(NULL);
bd->props.power = FB_BLANK_UNBLANK;
backlight_update_status(bd);
return 0;
}
static int eeepc_hwmon_init(struct device *dev)
{
struct device *hwmon;
int result;
hwmon = hwmon_device_register(dev);
if (IS_ERR(hwmon)) {
printk(EEEPC_ERR
"Could not register eeepc hwmon device\n");
eeepc_hwmon_device = NULL;
return PTR_ERR(hwmon);
}
eeepc_hwmon_device = hwmon;
result = sysfs_create_group(&hwmon->kobj,
&hwmon_attribute_group);
if (result)
eeepc_hwmon_exit();
return result;
}
static int __init eeepc_laptop_init(void)
{
struct device *dev;
int result;
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&eeepc_hotk_driver);
if (result < 0)
return result;
if (!ehotk) {
acpi_bus_unregister_driver(&eeepc_hotk_driver);
return -ENODEV;
}
dev = acpi_get_physical_device(ehotk->device->handle);
if (!acpi_video_backlight_support()) {
result = eeepc_backlight_init(dev);
if (result)
goto fail_backlight;
} else
printk(EEEPC_INFO "Backlight controlled by ACPI video "
"driver\n");
result = eeepc_hwmon_init(dev);
if (result)
goto fail_hwmon;
/* Register platform stuff */
result = platform_driver_register(&platform_driver);
if (result)
goto fail_platform_driver;
platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
if (!platform_device) {
result = -ENOMEM;
goto fail_platform_device1;
}
result = platform_device_add(platform_device);
if (result)
goto fail_platform_device2;
result = sysfs_create_group(&platform_device->dev.kobj,
&platform_attribute_group);
if (result)
goto fail_sysfs;
return 0;
fail_sysfs:
platform_device_del(platform_device);
fail_platform_device2:
platform_device_put(platform_device);
fail_platform_device1:
platform_driver_unregister(&platform_driver);
fail_platform_driver:
eeepc_hwmon_exit();
fail_hwmon:
eeepc_backlight_exit();
fail_backlight:
return result;
}
module_init(eeepc_laptop_init);
module_exit(eeepc_laptop_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,512 @@
/*
* HP WMI hotkeys
*
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
*
* Portions based on wistron_btns.c:
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
* Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
*
* 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
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/input.h>
#include <acpi/acpi_drivers.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/rfkill.h>
#include <linux/string.h>
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
#define HPWMI_DISPLAY_QUERY 0x1
#define HPWMI_HDDTEMP_QUERY 0x2
#define HPWMI_ALS_QUERY 0x3
#define HPWMI_DOCK_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5
#define HPWMI_HOTKEY_QUERY 0xc
static int __init hp_wmi_bios_setup(struct platform_device *device);
static int __exit hp_wmi_bios_remove(struct platform_device *device);
struct bios_args {
u32 signature;
u32 command;
u32 commandtype;
u32 datasize;
u32 data;
};
struct bios_return {
u32 sigpass;
u32 return_code;
u32 value;
};
struct key_entry {
char type; /* See KE_* below */
u16 code;
u16 keycode;
};
enum { KE_KEY, KE_SW, KE_END };
static struct key_entry hp_wmi_keymap[] = {
{KE_SW, 0x01, SW_DOCK},
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x20e6, KEY_PROG1},
{KE_KEY, 0x2142, KEY_MEDIA},
{KE_KEY, 0x213b, KEY_INFO},
{KE_KEY, 0x231b, KEY_HELP},
{KE_END, 0}
};
static struct input_dev *hp_wmi_input_dev;
static struct platform_device *hp_wmi_platform_dev;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
static struct platform_driver hp_wmi_driver = {
.driver = {
.name = "hp-wmi",
.owner = THIS_MODULE,
},
.probe = hp_wmi_bios_setup,
.remove = hp_wmi_bios_remove,
};
static int hp_wmi_perform_query(int query, int write, int value)
{
struct bios_return bios_return;
acpi_status status;
union acpi_object *obj;
struct bios_args args = {
.signature = 0x55434553,
.command = write ? 0x2 : 0x1,
.commandtype = query,
.datasize = write ? 0x4 : 0,
.data = value,
};
struct acpi_buffer input = { sizeof(struct bios_args), &args };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
obj = output.pointer;
if (!obj || obj->type != ACPI_TYPE_BUFFER)
return -EINVAL;
bios_return = *((struct bios_return *)obj->buffer.pointer);
if (bios_return.return_code > 0)
return bios_return.return_code * -1;
else
return bios_return.value;
}
static int hp_wmi_display_state(void)
{
return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
}
static int hp_wmi_hddtemp_state(void)
{
return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
}
static int hp_wmi_als_state(void)
{
return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
}
static int hp_wmi_dock_state(void)
{
return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
}
static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
}
static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
}
static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
{
if (state)
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
else
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
}
static int hp_wmi_wifi_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x100)
return RFKILL_STATE_UNBLOCKED;
else
return RFKILL_STATE_SOFT_BLOCKED;
}
static int hp_wmi_bluetooth_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x10000)
return RFKILL_STATE_UNBLOCKED;
else
return RFKILL_STATE_SOFT_BLOCKED;
}
static int hp_wmi_wwan_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x1000000)
return RFKILL_STATE_UNBLOCKED;
else
return RFKILL_STATE_SOFT_BLOCKED;
}
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_display_state();
if (value < 0)
return -EINVAL;
return sprintf(buf, "%d\n", value);
}
static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_hddtemp_state();
if (value < 0)
return -EINVAL;
return sprintf(buf, "%d\n", value);
}
static ssize_t show_als(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_als_state();
if (value < 0)
return -EINVAL;
return sprintf(buf, "%d\n", value);
}
static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_dock_state();
if (value < 0)
return -EINVAL;
return sprintf(buf, "%d\n", value);
}
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u32 tmp = simple_strtoul(buf, NULL, 10);
hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
return count;
}
static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static struct key_entry *hp_wmi_get_entry_by_scancode(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(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, int scancode, 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, int scancode, int keycode)
{
struct key_entry *key;
int old_keycode;
if (keycode < 0 || keycode > KEY_MAX)
return -EINVAL;
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;
wmi_get_event_data(value, &response);
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
int eventcode = *((u8 *) obj->buffer.pointer);
if (eventcode == 0x4)
eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
0);
key = hp_wmi_get_entry_by_scancode(eventcode);
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;
case KE_SW:
input_report_switch(hp_wmi_input_dev,
key->keycode,
hp_wmi_dock_state());
input_sync(hp_wmi_input_dev);
break;
}
} else if (eventcode == 0x5) {
if (wifi_rfkill)
rfkill_force_state(wifi_rfkill,
hp_wmi_wifi_state());
if (bluetooth_rfkill)
rfkill_force_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
if (wwan_rfkill)
rfkill_force_state(wwan_rfkill,
hp_wmi_wwan_state());
} else
printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
eventcode);
} else
printk(KERN_INFO "HP WMI: Unknown response received\n");
}
static int __init hp_wmi_input_setup(void)
{
struct key_entry *key;
int err;
hp_wmi_input_dev = input_allocate_device();
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;
case KE_SW:
set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(key->keycode, hp_wmi_input_dev->swbit);
break;
}
}
err = input_register_device(hp_wmi_input_dev);
if (err) {
input_free_device(hp_wmi_input_dev);
return err;
}
return 0;
}
static void cleanup_sysfs(struct platform_device *device)
{
device_remove_file(&device->dev, &dev_attr_display);
device_remove_file(&device->dev, &dev_attr_hddtemp);
device_remove_file(&device->dev, &dev_attr_als);
device_remove_file(&device->dev, &dev_attr_dock);
}
static int __init hp_wmi_bios_setup(struct platform_device *device)
{
int err;
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
err = device_create_file(&device->dev, &dev_attr_display);
if (err)
goto add_sysfs_error;
err = device_create_file(&device->dev, &dev_attr_hddtemp);
if (err)
goto add_sysfs_error;
err = device_create_file(&device->dev, &dev_attr_als);
if (err)
goto add_sysfs_error;
err = device_create_file(&device->dev, &dev_attr_dock);
if (err)
goto add_sysfs_error;
if (wireless & 0x1) {
wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
wifi_rfkill->name = "hp-wifi";
wifi_rfkill->state = hp_wmi_wifi_state();
wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
wifi_rfkill->user_claim_unsupported = 1;
rfkill_register(wifi_rfkill);
}
if (wireless & 0x2) {
bluetooth_rfkill = rfkill_allocate(&device->dev,
RFKILL_TYPE_BLUETOOTH);
bluetooth_rfkill->name = "hp-bluetooth";
bluetooth_rfkill->state = hp_wmi_bluetooth_state();
bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
bluetooth_rfkill->user_claim_unsupported = 1;
rfkill_register(bluetooth_rfkill);
}
if (wireless & 0x4) {
wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
wwan_rfkill->name = "hp-wwan";
wwan_rfkill->state = hp_wmi_wwan_state();
wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
wwan_rfkill->user_claim_unsupported = 1;
rfkill_register(wwan_rfkill);
}
return 0;
add_sysfs_error:
cleanup_sysfs(device);
return err;
}
static int __exit hp_wmi_bios_remove(struct platform_device *device)
{
cleanup_sysfs(device);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
if (wwan_rfkill)
rfkill_unregister(wwan_rfkill);
return 0;
}
static int __init hp_wmi_init(void)
{
int err;
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
hp_wmi_notify, NULL);
if (!err)
hp_wmi_input_setup();
}
if (wmi_has_guid(HPWMI_BIOS_GUID)) {
err = platform_driver_register(&hp_wmi_driver);
if (err)
return 0;
hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
if (!hp_wmi_platform_dev) {
platform_driver_unregister(&hp_wmi_driver);
return 0;
}
platform_device_add(hp_wmi_platform_dev);
}
return 0;
}
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 (hp_wmi_platform_dev) {
platform_device_del(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver);
}
}
module_init(hp_wmi_init);
module_exit(hp_wmi_exit);

View File

@@ -0,0 +1,536 @@
/*
* intel_menlow.c - Intel menlow Driver for thermal management extension
*
* Copyright (C) 2008 Intel Corp
* Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
* Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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; version 2 of the License.
*
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This driver creates the sys I/F for programming the sensors.
* It also implements the driver for intel menlow memory controller (hardware
* id is INT0002) which makes use of the platform specific ACPI methods
* to get/set bandwidth.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
MODULE_AUTHOR("Thomas Sujith");
MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Intel Menlow platform specific driver");
MODULE_LICENSE("GPL");
/*
* Memory controller device control
*/
#define MEMORY_GET_BANDWIDTH "GTHS"
#define MEMORY_SET_BANDWIDTH "STHS"
#define MEMORY_ARG_CUR_BANDWIDTH 1
#define MEMORY_ARG_MAX_BANDWIDTH 0
/*
* GTHS returning 'n' would mean that [0,n-1] states are supported
* In that case max_cstate would be n-1
* GTHS returning '0' would mean that no bandwidth control states are supported
*/
static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
unsigned long *max_state)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
unsigned long long value;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status = AE_OK;
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
&arg_list, &value);
if (ACPI_FAILURE(status))
return -EFAULT;
if (!value)
return -EINVAL;
*max_state = value - 1;
return 0;
}
static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
char *buf)
{
unsigned long value;
if (memory_get_int_max_bandwidth(cdev, &value))
return -EINVAL;
return sprintf(buf, "%ld\n", value);
}
static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
char *buf)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
unsigned long long value;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status = AE_OK;
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
&arg_list, &value);
if (ACPI_FAILURE(status))
return -EFAULT;
return sprintf(buf, "%llu\n", value);
}
static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
unsigned int state)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status;
unsigned long long temp;
unsigned long max_state;
if (memory_get_int_max_bandwidth(cdev, &max_state))
return -EFAULT;
if (state > max_state)
return -EINVAL;
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = state;
status =
acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
&temp);
printk(KERN_INFO
"Bandwidth value was %d: status is %d\n", state, status);
if (ACPI_FAILURE(status))
return -EFAULT;
return 0;
}
static struct thermal_cooling_device_ops memory_cooling_ops = {
.get_max_state = memory_get_max_bandwidth,
.get_cur_state = memory_get_cur_bandwidth,
.set_cur_state = memory_set_cur_bandwidth,
};
/*
* Memory Device Management
*/
static int intel_menlow_memory_add(struct acpi_device *device)
{
int result = -ENODEV;
acpi_status status = AE_OK;
acpi_handle dummy;
struct thermal_cooling_device *cdev;
if (!device)
return -EINVAL;
status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
if (ACPI_FAILURE(status))
goto end;
status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
if (ACPI_FAILURE(status))
goto end;
cdev = thermal_cooling_device_register("Memory controller", device,
&memory_cooling_ops);
if (IS_ERR(cdev)) {
result = PTR_ERR(cdev);
goto end;
}
device->driver_data = cdev;
result = sysfs_create_link(&device->dev.kobj,
&cdev->device.kobj, "thermal_cooling");
if (result)
goto unregister;
result = sysfs_create_link(&cdev->device.kobj,
&device->dev.kobj, "device");
if (result) {
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
goto unregister;
}
end:
return result;
unregister:
thermal_cooling_device_unregister(cdev);
return result;
}
static int intel_menlow_memory_remove(struct acpi_device *device, int type)
{
struct thermal_cooling_device *cdev = acpi_driver_data(device);
if (!device || !cdev)
return -EINVAL;
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
sysfs_remove_link(&cdev->device.kobj, "device");
thermal_cooling_device_unregister(cdev);
return 0;
}
static const struct acpi_device_id intel_menlow_memory_ids[] = {
{"INT0002", 0},
{"", 0},
};
static struct acpi_driver intel_menlow_memory_driver = {
.name = "intel_menlow_thermal_control",
.ids = intel_menlow_memory_ids,
.ops = {
.add = intel_menlow_memory_add,
.remove = intel_menlow_memory_remove,
},
};
/*
* Sensor control on menlow platform
*/
#define THERMAL_AUX0 0
#define THERMAL_AUX1 1
#define GET_AUX0 "GAX0"
#define GET_AUX1 "GAX1"
#define SET_AUX0 "SAX0"
#define SET_AUX1 "SAX1"
struct intel_menlow_attribute {
struct device_attribute attr;
struct device *device;
acpi_handle handle;
struct list_head node;
};
static LIST_HEAD(intel_menlow_attr_list);
static DEFINE_MUTEX(intel_menlow_attr_lock);
/*
* sensor_get_auxtrip - get the current auxtrip value from sensor
* @name: Thermalzone name
* @auxtype : AUX0/AUX1
* @buf: syfs buffer
*/
static int sensor_get_auxtrip(acpi_handle handle, int index,
unsigned long long *value)
{
acpi_status status;
if ((index != 0 && index != 1) || !value)
return -EINVAL;
status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
NULL, value);
if (ACPI_FAILURE(status))
return -EIO;
return 0;
}
/*
* sensor_set_auxtrip - set the new auxtrip value to sensor
* @name: Thermalzone name
* @auxtype : AUX0/AUX1
* @buf: syfs buffer
*/
static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
{
acpi_status status;
union acpi_object arg = {
ACPI_TYPE_INTEGER
};
struct acpi_object_list args = {
1, &arg
};
unsigned long long temp;
if (index != 0 && index != 1)
return -EINVAL;
status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
NULL, &temp);
if (ACPI_FAILURE(status))
return -EIO;
if ((index && value < temp) || (!index && value > temp))
return -EINVAL;
arg.integer.value = value;
status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
&args, &temp);
if (ACPI_FAILURE(status))
return -EIO;
/* do we need to check the return value of SAX0/SAX1 ? */
return 0;
}
#define to_intel_menlow_attr(_attr) \
container_of(_attr, struct intel_menlow_attribute, attr)
static ssize_t aux0_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
unsigned long long value;
int result;
result = sensor_get_auxtrip(attr->handle, 0, &value);
return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
}
static ssize_t aux1_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
unsigned long long value;
int result;
result = sensor_get_auxtrip(attr->handle, 1, &value);
return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
}
static ssize_t aux0_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
int value;
int result;
/*Sanity check; should be a positive integer */
if (!sscanf(buf, "%d", &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
return result ? result : count;
}
static ssize_t aux1_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
int value;
int result;
/*Sanity check; should be a positive integer */
if (!sscanf(buf, "%d", &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
return result ? result : count;
}
/* BIOS can enable/disable the thermal user application in dabney platform */
#define BIOS_ENABLED "\\_TZ.GSTS"
static ssize_t bios_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
acpi_status status;
unsigned long long bios_enabled;
status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
if (ACPI_FAILURE(status))
return -ENODEV;
return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
}
static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
void *store, struct device *dev,
acpi_handle handle)
{
struct intel_menlow_attribute *attr;
int result;
attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
if (!attr)
return -ENOMEM;
attr->attr.attr.name = name;
attr->attr.attr.mode = mode;
attr->attr.show = show;
attr->attr.store = store;
attr->device = dev;
attr->handle = handle;
result = device_create_file(dev, &attr->attr);
if (result)
return result;
mutex_lock(&intel_menlow_attr_lock);
list_add_tail(&attr->node, &intel_menlow_attr_list);
mutex_unlock(&intel_menlow_attr_lock);
return 0;
}
static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
void *context, void **rv)
{
acpi_status status;
acpi_handle dummy;
struct thermal_zone_device *thermal;
int result;
result = acpi_bus_get_private_data(handle, (void **)&thermal);
if (result)
return 0;
/* _TZ must have the AUX0/1 methods */
status = acpi_get_handle(handle, GET_AUX0, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
status = acpi_get_handle(handle, SET_AUX0, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
result = intel_menlow_add_one_attribute("aux0", 0644,
aux0_show, aux0_store,
&thermal->device, handle);
if (result)
return AE_ERROR;
status = acpi_get_handle(handle, GET_AUX1, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
status = acpi_get_handle(handle, SET_AUX1, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
result = intel_menlow_add_one_attribute("aux1", 0644,
aux1_show, aux1_store,
&thermal->device, handle);
if (result)
return AE_ERROR;
/*
* create the "dabney_enabled" attribute which means the user app
* should be loaded or not
*/
result = intel_menlow_add_one_attribute("bios_enabled", 0444,
bios_enabled_show, NULL,
&thermal->device, handle);
if (result)
return AE_ERROR;
not_found:
if (status == AE_NOT_FOUND)
return AE_OK;
else
return status;
}
static void intel_menlow_unregister_sensor(void)
{
struct intel_menlow_attribute *pos, *next;
mutex_lock(&intel_menlow_attr_lock);
list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
list_del(&pos->node);
device_remove_file(pos->device, &pos->attr);
kfree(pos);
}
mutex_unlock(&intel_menlow_attr_lock);
return;
}
static int __init intel_menlow_module_init(void)
{
int result = -ENODEV;
acpi_status status;
unsigned long long enable;
if (acpi_disabled)
return result;
/* Looking for the \_TZ.GSTS method */
status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
if (ACPI_FAILURE(status) || !enable)
return -ENODEV;
/* Looking for ACPI device MEM0 with hardware id INT0002 */
result = acpi_bus_register_driver(&intel_menlow_memory_driver);
if (result)
return result;
/* Looking for sensors in each ACPI thermal zone */
status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
intel_menlow_register_sensor, NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
}
static void __exit intel_menlow_module_exit(void)
{
acpi_bus_unregister_driver(&intel_menlow_memory_driver);
intel_menlow_unregister_sensor();
}
module_init(intel_menlow_module_init);
module_exit(intel_menlow_module_exit);

View File

@@ -0,0 +1,437 @@
/*-*-linux-c-*-*/
/*
Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
/*
* msi-laptop.c - MSI S270 laptop support. This laptop is sold under
* various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
*
* Driver also supports S271, S420 models.
*
* This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
*
* lcd_level - Screen brightness: contains a single integer in the
* range 0..8. (rw)
*
* auto_brightness - Enable automatic brightness control: contains
* either 0 or 1. If set to 1 the hardware adjusts the screen
* brightness automatically when the power cord is
* plugged/unplugged. (rw)
*
* wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
*
* bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
* Please note that this file is constantly 0 if no Bluetooth
* hardware is available. (ro)
*
* In addition to these platform device attributes the driver
* registers itself in the Linux backlight control subsystem and is
* available to userspace under /sys/class/backlight/msi-laptop-bl/.
*
* This driver might work on other laptops produced by MSI. If you
* want to try it you can pass force=1 as argument to the module which
* will force it to load even when the DMI data doesn't identify the
* laptop as MSI S270. YMMV.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#define MSI_DRIVER_VERSION "0.5"
#define MSI_LCD_LEVEL_MAX 9
#define MSI_EC_COMMAND_WIRELESS 0x10
#define MSI_EC_COMMAND_LCD_LEVEL 0x11
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
static int auto_brightness;
module_param(auto_brightness, int, 0);
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
/* Hardware access */
static int set_lcd_level(int level)
{
u8 buf[2];
if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
return -EINVAL;
buf[0] = 0x80;
buf[1] = (u8) (level*31);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
}
static int get_lcd_level(void)
{
u8 wdata = 0, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
if (result < 0)
return result;
return (int) rdata / 31;
}
static int get_auto_brightness(void)
{
u8 wdata = 4, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
if (result < 0)
return result;
return !!(rdata & 8);
}
static int set_auto_brightness(int enable)
{
u8 wdata[2], rdata;
int result;
wdata[0] = 4;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
if (result < 0)
return result;
wdata[0] = 0x84;
wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
}
static int get_wireless_state(int *wlan, int *bluetooth)
{
u8 wdata = 0, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
if (result < 0)
return -1;
if (wlan)
*wlan = !!(rdata & 8);
if (bluetooth)
*bluetooth = !!(rdata & 128);
return 0;
}
/* Backlight device stuff */
static int bl_get_brightness(struct backlight_device *b)
{
return get_lcd_level();
}
static int bl_update_status(struct backlight_device *b)
{
return set_lcd_level(b->props.brightness);
}
static struct backlight_ops msibl_ops = {
.get_brightness = bl_get_brightness,
.update_status = bl_update_status,
};
static struct backlight_device *msibl_device;
/* Platform device */
static ssize_t show_wlan(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(&enabled, NULL);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t show_bluetooth(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(NULL, &enabled);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t show_lcd_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = get_lcd_level();
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
}
static ssize_t store_lcd_level(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level, ret;
if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
return -EINVAL;
ret = set_lcd_level(level);
if (ret < 0)
return ret;
return count;
}
static ssize_t show_auto_brightness(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = get_auto_brightness();
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
}
static ssize_t store_auto_brightness(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int enable, ret;
if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
return -EINVAL;
ret = set_auto_brightness(enable);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
static struct attribute *msipf_attributes[] = {
&dev_attr_lcd_level.attr,
&dev_attr_auto_brightness.attr,
&dev_attr_bluetooth.attr,
&dev_attr_wlan.attr,
NULL
};
static struct attribute_group msipf_attribute_group = {
.attrs = msipf_attributes
};
static struct platform_driver msipf_driver = {
.driver = {
.name = "msi-laptop-pf",
.owner = THIS_MODULE,
}
};
static struct platform_device *msipf_device;
/* Initialization */
static int dmi_check_cb(const struct dmi_system_id *id)
{
printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
return 0;
}
static struct dmi_system_id __initdata msi_dmi_table[] = {
{
.ident = "MSI S270",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
},
.callback = dmi_check_cb
},
{
.ident = "MSI S271",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
},
.callback = dmi_check_cb
},
{
.ident = "MSI S420",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
},
.callback = dmi_check_cb
},
{
.ident = "Medion MD96100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
},
.callback = dmi_check_cb
},
{ }
};
static int __init msi_init(void)
{
int ret;
if (acpi_disabled)
return -ENODEV;
if (!force && !dmi_check_system(msi_dmi_table))
return -ENODEV;
if (auto_brightness < 0 || auto_brightness > 2)
return -EINVAL;
/* Register backlight stuff */
if (acpi_video_backlight_support()) {
printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
"by ACPI video driver\n");
} else {
msibl_device = backlight_device_register("msi-laptop-bl", NULL,
NULL, &msibl_ops);
if (IS_ERR(msibl_device))
return PTR_ERR(msibl_device);
msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
}
ret = platform_driver_register(&msipf_driver);
if (ret)
goto fail_backlight;
/* Register platform stuff */
msipf_device = platform_device_alloc("msi-laptop-pf", -1);
if (!msipf_device) {
ret = -ENOMEM;
goto fail_platform_driver;
}
ret = platform_device_add(msipf_device);
if (ret)
goto fail_platform_device1;
ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
if (ret)
goto fail_platform_device2;
/* Disable automatic brightness control by default because
* this module was probably loaded to do brightness control in
* software. */
if (auto_brightness != 2)
set_auto_brightness(auto_brightness);
printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
return 0;
fail_platform_device2:
platform_device_del(msipf_device);
fail_platform_device1:
platform_device_put(msipf_device);
fail_platform_driver:
platform_driver_unregister(&msipf_driver);
fail_backlight:
backlight_device_unregister(msibl_device);
return ret;
}
static void __exit msi_cleanup(void)
{
sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
platform_device_unregister(msipf_device);
platform_driver_unregister(&msipf_driver);
backlight_device_unregister(msibl_device);
/* Enable automatic brightness control again */
if (auto_brightness != 2)
set_auto_brightness(1);
printk(KERN_INFO "msi-laptop: driver unloaded.\n");
}
module_init(msi_init);
module_exit(msi_cleanup);
MODULE_AUTHOR("Lennart Poettering");
MODULE_DESCRIPTION("MSI Laptop Support");
MODULE_VERSION(MSI_DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");

View File

@@ -0,0 +1,766 @@
/*
* Panasonic HotKey and LCD brightness control driver
* (C) 2004 Hiroshi Miura <miura@da-cha.org>
* (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
* (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
* (C) 2004 David Bronaugh <dbronaugh>
* (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
*
* derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* publicshed by the Free Software Foundation.
*
* 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
*
*---------------------------------------------------------------------------
*
* ChangeLog:
* Sep.23, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.95 rename driver from drivers/acpi/pcc_acpi.c to
* drivers/misc/panasonic-laptop.c
*
* Jul.04, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.94 replace /proc interface with device attributes
* support {set,get}keycode on th input device
*
* Jun.27, 2008 Harald Welte <laforge@gnumonks.org>
* -v0.92 merge with 2.6.26-rc6 input API changes
* remove broken <= 2.6.15 kernel support
* resolve all compiler warnings
* various coding style fixes (checkpatch.pl)
* add support for backlight api
* major code restructuring
*
* Dac.28, 2007 Harald Welte <laforge@gnumonks.org>
* -v0.91 merge with 2.6.24-rc6 ACPI changes
*
* Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>
* -v0.9 remove warning about section reference.
* remove acpi_os_free
* add /proc/acpi/pcc/brightness interface for HAL access
* merge dbronaugh's enhancement
* Aug.17, 2004 David Bronaugh (dbronaugh)
* - Added screen brightness setting interface
* Thanks to FreeBSD crew (acpi_panasonic.c)
* for the ideas I needed to accomplish it
*
* May.29, 2006 Hiroshi Miura <miura@da-cha.org>
* -v0.8.4 follow to change keyinput structure
* thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
* Jacob Bower <jacob.bower@ic.ac.uk> and
* Hiroshi Yokota for providing solutions.
*
* Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.8.2 merge code of YOKOTA Hiroshi
* <yokota@netlab.is.tsukuba.ac.jp>.
* Add sticky key mode interface.
* Refactoring acpi_pcc_generate_keyinput().
*
* Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.8 Generate key input event on input subsystem.
* This is based on yet another driver written by
* Ryuta Nakanishi.
*
* Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.7 Change proc interface functions using seq_file
* facility as same as other ACPI drivers.
*
* Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.6.4 Fix a silly error with status checking
*
* Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>
* -v0.6.3 replace read_acpi_int by standard function
* acpi_evaluate_integer
* some clean up and make smart copyright notice.
* fix return value of pcc_acpi_get_key()
* fix checking return value of acpi_bus_register_driver()
*
* Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* -v0.6.2 Add check on ACPI data (num_sifr)
* Coding style cleanups, better error messages/handling
* Fixed an off-by-one error in memory allocation
*
* Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* -v0.6.1 Fix a silly error with status checking
*
* Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
* - v0.6 Correct brightness controls to reflect reality
* based on information gleaned by Hiroshi Miura
* and discussions with Hiroshi Miura
*
* Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.5 support LCD brightness control
* based on the disclosed information by MEI.
*
* Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.4 first post version
* add function to retrive SIFR
*
* Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.3 get proper status of hotkey
*
* Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.2 add HotKey handler
*
* Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>
* - v0.1 start from toshiba_acpi driver written by John Belmonte
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/backlight.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <linux/input.h>
#ifndef ACPI_HOTKEY_COMPONENT
#define ACPI_HOTKEY_COMPONENT 0x10000000
#endif
#define _COMPONENT ACPI_HOTKEY_COMPONENT
MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
MODULE_LICENSE("GPL");
#define LOGPREFIX "pcc_acpi: "
/* Define ACPI PATHs */
/* Lets note hotkeys */
#define METHOD_HKEY_QUERY "HINF"
#define METHOD_HKEY_SQTY "SQTY"
#define METHOD_HKEY_SINF "SINF"
#define METHOD_HKEY_SSET "SSET"
#define HKEY_NOTIFY 0x80
#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
#define ACPI_PCC_DEVICE_NAME "Hotkey"
#define ACPI_PCC_CLASS "pcc"
#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
*/
enum SINF_BITS { SINF_NUM_BATTERIES = 0,
SINF_LCD_TYPE,
SINF_AC_MAX_BRIGHT,
SINF_AC_MIN_BRIGHT,
SINF_AC_CUR_BRIGHT,
SINF_DC_MAX_BRIGHT,
SINF_DC_MIN_BRIGHT,
SINF_DC_CUR_BRIGHT,
SINF_MUTE,
SINF_RESERVED,
SINF_ENV_STATE,
SINF_STICKY_KEY = 0x80,
};
/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
static int acpi_pcc_hotkey_add(struct acpi_device *device);
static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
static int acpi_pcc_hotkey_resume(struct acpi_device *device);
static const struct acpi_device_id pcc_device_ids[] = {
{ "MAT0012", 0},
{ "MAT0013", 0},
{ "MAT0018", 0},
{ "MAT0019", 0},
{ "", 0},
};
static struct acpi_driver acpi_pcc_driver = {
.name = ACPI_PCC_DRIVER_NAME,
.class = ACPI_PCC_CLASS,
.ids = pcc_device_ids,
.ops = {
.add = acpi_pcc_hotkey_add,
.remove = acpi_pcc_hotkey_remove,
.resume = acpi_pcc_hotkey_resume,
},
};
#define KEYMAP_SIZE 11
static const 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,
};
struct pcc_acpi {
acpi_handle handle;
unsigned long num_sifr;
int sticky_mode;
u32 *sinf;
struct acpi_device *device;
struct input_dev *input_dev;
struct backlight_device *backlight;
int keymap[KEYMAP_SIZE];
};
struct pcc_keyinput {
struct acpi_hotkey *hotkey;
};
/* method access functions */
static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
{
union acpi_object in_objs[] = {
{ .integer.type = ACPI_TYPE_INTEGER,
.integer.value = func, },
{ .integer.type = ACPI_TYPE_INTEGER,
.integer.value = val, },
};
struct acpi_object_list params = {
.count = ARRAY_SIZE(in_objs),
.pointer = in_objs,
};
acpi_status status = AE_OK;
ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
&params, NULL);
return status == AE_OK;
}
static inline int acpi_pcc_get_sqty(struct acpi_device *device)
{
unsigned long long s;
acpi_status status;
ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
NULL, &s);
if (ACPI_SUCCESS(status))
return s;
else {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"evaluation error HKEY.SQTY\n"));
return -EINVAL;
}
}
static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
{
acpi_status status;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *hkey = NULL;
int i;
ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
&buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"evaluation error HKEY.SINF\n"));
return 0;
}
hkey = buffer.pointer;
if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
goto end;
}
if (pcc->num_sifr < hkey->package.count) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"SQTY reports bad SINF length\n"));
status = AE_ERROR;
goto end;
}
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;
} else
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Invalid HKEY.SINF data\n"));
}
sinf[hkey->package.count] = -1;
end:
kfree(buffer.pointer);
return status == AE_OK;
}
/* backlight API interface functions */
/* This driver currently treats AC and DC brightness identical,
* since we don't need to invent an interface to the core ACPI
* logic to receive events in case a power supply is plugged in
* or removed */
static int bl_get(struct backlight_device *bd)
{
struct pcc_acpi *pcc = bl_get_data(bd);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
return pcc->sinf[SINF_AC_CUR_BRIGHT];
}
static int bl_set_status(struct backlight_device *bd)
{
struct pcc_acpi *pcc = bl_get_data(bd);
int bright = bd->props.brightness;
int rc;
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
return -EINVAL;
rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
if (rc < 0)
return rc;
return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
}
static struct backlight_ops pcc_backlight_ops = {
.get_brightness = bl_get,
.update_status = bl_set_status,
};
/* sysfs user interface functions */
static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
}
static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
}
static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
}
static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
return -EIO;
return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
}
static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *acpi = to_acpi_device(dev);
struct pcc_acpi *pcc = acpi_driver_data(acpi);
int val;
if (count && sscanf(buf, "%i", &val) == 1 &&
(val == 0 || val == 1)) {
acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
pcc->sticky_mode = val;
}
return count;
}
static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
static struct attribute *pcc_sysfs_entries[] = {
&dev_attr_numbatt.attr,
&dev_attr_lcdtype.attr,
&dev_attr_mute.attr,
&dev_attr_sticky_key.attr,
NULL,
};
static struct attribute_group pcc_attr_group = {
.name = NULL, /* put in device directory */
.attrs = pcc_sysfs_entries,
};
/* hotkey input device driver */
static int pcc_getkeycode(struct input_dev *dev, int scancode, 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, 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, int scancode, int keycode)
{
struct pcc_acpi *pcc = input_get_drvdata(dev);
int oldkeycode;
if (scancode >= ARRAY_SIZE(pcc->keymap))
return -EINVAL;
if (keycode < 0 || keycode > KEY_MAX)
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;
ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
NULL, &result);
if (!ACPI_SUCCESS(rc)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"error getting hotkey status\n"));
return;
}
acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
hkey_num = result & 0xf;
if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
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;
}
static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
{
struct pcc_acpi *pcc = (struct pcc_acpi *) data;
ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
switch (event) {
case HKEY_NOTIFY:
acpi_pcc_generate_keyinput(pcc);
break;
default:
/* nothing to do */
break;
}
}
static int acpi_pcc_init_input(struct pcc_acpi *pcc)
{
int i, rc;
ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
pcc->input_dev = input_allocate_device();
if (!pcc->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);
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;
/* load initial keymap */
memcpy(pcc->keymap, initial_keymap, sizeof(pcc->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);
input_set_drvdata(pcc->input_dev, pcc);
rc = input_register_device(pcc->input_dev);
if (rc < 0)
input_free_device(pcc->input_dev);
return rc;
}
/* kernel module interface */
static int acpi_pcc_hotkey_resume(struct acpi_device *device)
{
struct pcc_acpi *pcc = acpi_driver_data(device);
acpi_status status = AE_OK;
ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
if (device == NULL || pcc == NULL)
return -EINVAL;
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
pcc->sticky_mode));
status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
return status == AE_OK ? 0 : -EINVAL;
}
static int acpi_pcc_hotkey_add(struct acpi_device *device)
{
acpi_status status;
struct pcc_acpi *pcc;
int num_sifr, result;
ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
if (!device)
return -EINVAL;
num_sifr = acpi_pcc_get_sqty(device);
if (num_sifr > 255) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
return -ENODEV;
}
pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
if (!pcc) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't allocate mem for pcc"));
return -ENOMEM;
}
pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
if (!pcc->sinf) {
result = -ENOMEM;
goto out_hotkey;
}
pcc->device = device;
pcc->handle = device->handle;
pcc->num_sifr = num_sifr;
device->driver_data = pcc;
strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
result = acpi_pcc_init_input(pcc);
if (result) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error installing keyinput handler\n"));
goto out_sinf;
}
/* initialize hotkey input device */
status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
acpi_pcc_hotkey_notify, pcc);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error installing notify handler\n"));
result = -ENODEV;
goto out_input;
}
/* initialize backlight */
pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
&pcc_backlight_ops);
if (IS_ERR(pcc->backlight))
goto out_notify;
if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't retrieve BIOS data\n"));
goto out_backlight;
}
/* read the initial brightness setting from the hardware */
pcc->backlight->props.max_brightness =
pcc->sinf[SINF_AC_MAX_BRIGHT];
pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
/* read the initial sticky key mode from the hardware */
pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
/* add sysfs attributes */
result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
if (result)
goto out_backlight;
return 0;
out_backlight:
backlight_device_unregister(pcc->backlight);
out_notify:
acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
acpi_pcc_hotkey_notify);
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_sinf:
kfree(pcc->sinf);
out_hotkey:
kfree(pcc);
return result;
}
static int __init acpi_pcc_init(void)
{
int result = 0;
ACPI_FUNCTION_TRACE("acpi_pcc_init");
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&acpi_pcc_driver);
if (result < 0) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Error registering hotkey driver\n"));
return -ENODEV;
}
return 0;
}
static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
{
struct pcc_acpi *pcc = acpi_driver_data(device);
ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
if (!device || !pcc)
return -EINVAL;
sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
backlight_device_unregister(pcc->backlight);
acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
acpi_pcc_hotkey_notify);
input_unregister_device(pcc->input_dev);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
kfree(pcc->sinf);
kfree(pcc);
return 0;
}
static void __exit acpi_pcc_exit(void)
{
ACPI_FUNCTION_TRACE("acpi_pcc_exit");
acpi_bus_unregister_driver(&acpi_pcc_driver);
}
module_init(acpi_pcc_init);
module_exit(acpi_pcc_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,290 @@
/*
* HP Compaq TC1100 Tablet WMI Extras Driver
*
* Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
* Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <acpi/acpi.h>
#include <acpi/actypes.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include <linux/platform_device.h>
#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
#define TC1100_INSTANCE_WIRELESS 1
#define TC1100_INSTANCE_JOGDIAL 2
#define TC1100_LOGPREFIX "tc1100-wmi: "
#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
static int tc1100_probe(struct platform_device *device);
static int tc1100_remove(struct platform_device *device);
static int tc1100_suspend(struct platform_device *device, pm_message_t state);
static int tc1100_resume(struct platform_device *device);
static struct platform_driver tc1100_driver = {
.driver = {
.name = "tc1100-wmi",
.owner = THIS_MODULE,
},
.probe = tc1100_probe,
.remove = tc1100_remove,
.suspend = tc1100_suspend,
.resume = tc1100_resume,
};
static struct platform_device *tc1100_device;
struct tc1100_data {
u32 wireless;
u32 jogdial;
};
static struct tc1100_data suspend_data;
/* --------------------------------------------------------------------------
Device Management
-------------------------------------------------------------------------- */
static int get_state(u32 *out, u8 instance)
{
u32 tmp;
acpi_status status;
struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
if (!out)
return -EINVAL;
if (instance > 2)
return -ENODEV;
status = wmi_query_block(GUID, instance, &result);
if (ACPI_FAILURE(status))
return -ENODEV;
obj = (union acpi_object *) result.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER &&
obj->buffer.length == sizeof(u32)) {
tmp = *((u32 *) obj->buffer.pointer);
} else {
tmp = 0;
}
if (result.length > 0 && result.pointer)
kfree(result.pointer);
switch (instance) {
case TC1100_INSTANCE_WIRELESS:
*out = (tmp == 3) ? 1 : 0;
return 0;
case TC1100_INSTANCE_JOGDIAL:
*out = (tmp == 1) ? 1 : 0;
return 0;
default:
return -ENODEV;
}
}
static int set_state(u32 *in, u8 instance)
{
u32 value;
acpi_status status;
struct acpi_buffer input;
if (!in)
return -EINVAL;
if (instance > 2)
return -ENODEV;
switch (instance) {
case TC1100_INSTANCE_WIRELESS:
value = (*in) ? 1 : 2;
break;
case TC1100_INSTANCE_JOGDIAL:
value = (*in) ? 0 : 1;
break;
default:
return -ENODEV;
}
input.length = sizeof(u32);
input.pointer = &value;
status = wmi_set_block(GUID, instance, &input);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
}
/* --------------------------------------------------------------------------
FS Interface (/sys)
-------------------------------------------------------------------------- */
/*
* Read/ write bool sysfs macro
*/
#define show_set_bool(value, instance) \
static ssize_t \
show_bool_##value(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
u32 result; \
acpi_status status = get_state(&result, instance); \
if (ACPI_SUCCESS(status)) \
return sprintf(buf, "%d\n", result); \
return sprintf(buf, "Read error\n"); \
} \
\
static ssize_t \
set_bool_##value(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
u32 tmp = simple_strtoul(buf, NULL, 10); \
acpi_status status = set_state(&tmp, instance); \
if (ACPI_FAILURE(status)) \
return -EINVAL; \
return count; \
} \
static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
show_bool_##value, set_bool_##value);
show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
static void remove_fs(void)
{
device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
}
static int add_fs(void)
{
int ret;
ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
if (ret)
goto add_sysfs_error;
ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
if (ret)
goto add_sysfs_error;
return ret;
add_sysfs_error:
remove_fs();
return ret;
}
/* --------------------------------------------------------------------------
Driver Model
-------------------------------------------------------------------------- */
static int tc1100_probe(struct platform_device *device)
{
int result = 0;
result = add_fs();
return result;
}
static int tc1100_remove(struct platform_device *device)
{
remove_fs();
return 0;
}
static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
{
int ret;
ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
if (ret)
return ret;
ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
if (ret)
return ret;
return ret;
}
static int tc1100_resume(struct platform_device *dev)
{
int ret;
ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
if (ret)
return ret;
ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
if (ret)
return ret;
return ret;
}
static int __init tc1100_init(void)
{
int result = 0;
if (!wmi_has_guid(GUID))
return -ENODEV;
result = platform_driver_register(&tc1100_driver);
if (result)
return result;
tc1100_device = platform_device_alloc("tc1100-wmi", -1);
platform_device_add(tc1100_device);
printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
return result;
}
static void __exit tc1100_exit(void)
{
platform_device_del(tc1100_device);
platform_driver_unregister(&tc1100_driver);
printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
}
module_init(tc1100_init);
module_exit(tc1100_exit);

File diff suppressed because it is too large Load Diff