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:
5
drivers/platform/Kconfig
Normal file
5
drivers/platform/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
# drivers/platform/Kconfig
|
||||
|
||||
if X86
|
||||
source "drivers/platform/x86/Kconfig"
|
||||
endif
|
5
drivers/platform/Makefile
Normal file
5
drivers/platform/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for linux/drivers/platform
|
||||
#
|
||||
|
||||
obj-$(CONFIG_X86) += x86/
|
290
drivers/platform/x86/Kconfig
Normal file
290
drivers/platform/x86/Kconfig
Normal 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
|
16
drivers/platform/x86/Makefile
Normal file
16
drivers/platform/x86/Makefile
Normal 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
|
1345
drivers/platform/x86/acer-wmi.c
Normal file
1345
drivers/platform/x86/acer-wmi.c
Normal file
File diff suppressed because it is too large
Load Diff
1266
drivers/platform/x86/asus-laptop.c
Normal file
1266
drivers/platform/x86/asus-laptop.c
Normal file
File diff suppressed because it is too large
Load Diff
406
drivers/platform/x86/compal-laptop.c
Normal file
406
drivers/platform/x86/compal-laptop.c
Normal 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:*");
|
872
drivers/platform/x86/eeepc-laptop.c
Normal file
872
drivers/platform/x86/eeepc-laptop.c
Normal 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, ¶ms, 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);
|
1126
drivers/platform/x86/fujitsu-laptop.c
Normal file
1126
drivers/platform/x86/fujitsu-laptop.c
Normal file
File diff suppressed because it is too large
Load Diff
512
drivers/platform/x86/hp-wmi.c
Normal file
512
drivers/platform/x86/hp-wmi.c
Normal 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);
|
536
drivers/platform/x86/intel_menlow.c
Normal file
536
drivers/platform/x86/intel_menlow.c
Normal 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);
|
437
drivers/platform/x86/msi-laptop.c
Normal file
437
drivers/platform/x86/msi-laptop.c
Normal 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:*");
|
766
drivers/platform/x86/panasonic-laptop.c
Normal file
766
drivers/platform/x86/panasonic-laptop.c
Normal 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,
|
||||
¶ms, 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);
|
2781
drivers/platform/x86/sony-laptop.c
Normal file
2781
drivers/platform/x86/sony-laptop.c
Normal file
File diff suppressed because it is too large
Load Diff
290
drivers/platform/x86/tc1100-wmi.c
Normal file
290
drivers/platform/x86/tc1100-wmi.c
Normal 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);
|
6949
drivers/platform/x86/thinkpad_acpi.c
Normal file
6949
drivers/platform/x86/thinkpad_acpi.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user