add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,444 @@
#
# 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"
depends on ACPI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
depends on RFKILL || RFKILL = n
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 ACERHDF
tristate "Acer Aspire One temperature and fan driver"
depends on THERMAL && THERMAL_HWMON && ACPI
---help---
This is a driver for Acer Aspire One netbooks. It allows to access
the temperature sensor and to control the fan.
After loading this driver the BIOS is still in control of the fan.
To let the kernel handle the fan, do:
echo -n enabled > /sys/class/thermal/thermal_zone0/mode
For more information about this driver see
<http://piie.net/files/acerhdf_README.txt>
If you have an Acer Aspire One netbook, say Y or M
here.
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
depends on !ACPI_ASUS
select LEDS_CLASS
select NEW_LEDS
select BACKLIGHT_CLASS_DEVICE
depends on INPUT
---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 and input 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 DELL_LAPTOP
tristate "Dell Laptop Extras (EXPERIMENTAL)"
depends on X86
depends on DCDBAS
depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on POWER_SUPPLY
default n
---help---
This driver adds support for rfkill and backlight control to Dell
laptops.
config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
depends on INPUT
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
To compile this driver as a module, choose M here: the module will
be called dell-wmi.
config FUJITSU_LAPTOP
tristate "Fujitsu Laptop Extras"
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
depends on LEDS_CLASS || LEDS_CLASS=n
---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 || RFKILL = n
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
depends on RFKILL
---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
depends on INPUT
depends on RFKILL || RFKILL = n
select BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
select NEW_LEDS
select LEDS_CLASS
---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_DEBUGFACILITIES
bool "Maintainer debug facilities"
depends on THINKPAD_ACPI
default n
---help---
Enables extra stuff in the thinkpad-acpi which is completely useless
for normal use. Read the driver source to find out what it does.
Say N here, unless you were told by a kernel maintainer to do
otherwise.
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_UNSAFE_LEDS
bool "Allow control of important LEDs (unsafe)"
depends on THINKPAD_ACPI
default n
---help---
Overriding LED state on ThinkPads can mask important
firmware alerts (like critical battery condition), or misled
the user into damaging the hardware (undocking or ejecting
the bay while buses are still active), etc.
LED control on the ThinkPad is write-only (with very few
exceptions on very ancient models), which makes it
impossible to know beforehand if important information will
be lost when one changes LED state.
Users that know what they are doing can enable this option
and the driver will allow control of every LED, including
the ones on the dock stations.
Never enable this option on a distribution kernel.
Say N here, unless you are building a kernel for your own
use, and need to control the important firmware LEDs.
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 memory and avoid
bad interactions with X.org.
NOTE: access to this feature is limited to processes with the
CAP_SYS_ADMIN capability, to avoid local DoS issues in platforms
where it interacts badly with X.org.
If you are not sure, say Y here but do try to check if you could
be using X.org RandR instead.
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 INPUT
depends on EXPERIMENTAL
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
select BACKLIGHT_CLASS_DEVICE
select HWMON
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also gives access to some extra laptop functionalities like
Bluetooth, backlight and allows powering on/off some other
devices.
If you have an Eee PC laptop, say Y or M here.
config ACPI_WMI
tristate "WMI"
depends on ACPI
help
This driver adds support for the ACPI-WMI (Windows Management
Instrumentation) mapper device (PNP0C14) found on some systems.
ACPI-WMI is a proprietary extension to ACPI to expose parts of the
ACPI firmware to userspace - this is done through various vendor
defined methods and data blocks in a PNP0C14 device, which are then
made available for userspace to call.
The implementation of this in Linux currently only exposes this to
other kernel space drivers.
This driver is a required dependency to build the firmware specific
drivers needed on many machines, including Acer and HP laptops.
It is safe to enable this driver even if your DSDT doesn't define
any ACPI-WMI devices.
config ACPI_ASUS
tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
depends on ACPI
select BACKLIGHT_CLASS_DEVICE
---help---
This driver provides support for extra features of ACPI-compatible
ASUS laptops. As some of Medion laptops are made by ASUS, it may also
support some Medion laptops (such as 9675 for example). It makes all
the extra buttons generate standard ACPI events that go through
/proc/acpi/events, and (on some models) adds support for changing the
display brightness and output, switching the LCD backlight on and off,
and most importantly, allows you to blink those fancy LEDs intended
for reporting mail and wireless status.
Note: display switching code is currently considered EXPERIMENTAL,
toying with these values may even lock your machine.
All settings are changed via /proc/acpi/asus directory entries. Owner
and group for these entries can be set with asus_uid and asus_gid
parameters.
More information and a userspace daemon for handling the extra buttons
at <http://acpi4asus.sf.net>.
If you have an ACPI-compatible ASUS laptop, say Y or M here. This
driver is still under development, so if your laptop is unsupported or
something works not quite as expected, please use the mailing list
available on the above page (acpi4asus-user@lists.sourceforge.net).
NOTE: This driver is deprecated and will probably be removed soon,
use asus-laptop instead.
config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
depends on INPUT
---help---
This driver adds support for hotkeys found on Topstar laptops.
If you have a Topstar laptop, say Y or M here.
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
select INPUT_POLLDEV
select BACKLIGHT_CLASS_DEVICE
---help---
This driver adds support for access to certain system settings
on "legacy free" Toshiba laptops. These laptops can be recognized by
their lack of a BIOS setup menu and APM support.
On these machines, all system configuration is handled through the
ACPI. This driver is required for access to controls not covered
by the general ACPI drivers, such as LCD brightness, video output,
etc.
This driver differs from the non-ACPI Toshiba laptop driver (located
under "Processor type and features") in several aspects.
Configuration is accessed by reading and writing text files in the
/proc tree instead of by program interface to /dev. Furthermore, no
power management functions are exposed, as those are handled by the
general ACPI drivers.
More information about this driver is available at
<http://memebeam.org/toys/ToshibaAcpiDriver>.
If you have a legacy free Toshiba laptop (such as the Libretto L1
series), say Y.
endif # X86_PLATFORM_DEVICES

View File

@@ -0,0 +1,23 @@
#
# 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_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_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
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,649 @@
/*
* acerhdf - A driver which monitors the temperature
* of the aspire one netbook, turns on/off the fan
* as soon as the upper/lower threshold is reached.
*
* (C) 2009 - Peter Feuerer peter (a) piie.net
* http://piie.net
* 2009 Borislav Petkov <petkovbb@gmail.com>
*
* Inspired by and many thanks to:
* o acerfand - Rachel Greenham
* o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com
* - Petr Tomasek tomasek (#) etf,cuni,cz
* - Carlos Corbacho cathectic (at) gmail.com
* o lkml - Matthew Garrett
* - Borislav Petkov
* - Andreas Mohr
*
* 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
*/
#define pr_fmt(fmt) "acerhdf: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/dmi.h>
#include <acpi/acpi_drivers.h>
#include <linux/sched.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
/*
* The driver is started with "kernel mode off" by default. That means, the BIOS
* is still in control of the fan. In this mode the driver allows to read the
* temperature of the cpu and a userspace tool may take over control of the fan.
* If the driver is switched to "kernel mode" (e.g. via module parameter) the
* driver is in full control of the fan. If you want the module to be started in
* kernel mode by default, define the following:
*/
#undef START_IN_KERNEL_MODE
#define DRV_VER "0.5.20"
/*
* According to the Atom N270 datasheet,
* (http://download.intel.com/design/processor/datashts/320032.pdf) the
* CPU's optimal operating limits denoted in junction temperature as
* measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
* assume 89°C is critical temperature.
*/
#define ACERHDF_TEMP_CRIT 89000
#define ACERHDF_FAN_OFF 0
#define ACERHDF_FAN_AUTO 1
/*
* No matter what value the user puts into the fanon variable, turn on the fan
* at 80 degree Celsius to prevent hardware damage
*/
#define ACERHDF_MAX_FANON 80000
/*
* Maximum interval between two temperature checks is 15 seconds, as the die
* can get hot really fast under heavy load (plus we shouldn't forget about
* possible impact of _external_ aggressive sources such as heaters, sun etc.)
*/
#define ACERHDF_MAX_INTERVAL 15
#ifdef START_IN_KERNEL_MODE
static int kernelmode = 1;
#else
static int kernelmode;
#endif
static unsigned int interval = 10;
static unsigned int fanon = 63000;
static unsigned int fanoff = 58000;
static unsigned int verbose;
static unsigned int fanstate = ACERHDF_FAN_AUTO;
static char force_bios[16];
static char force_product[16];
static unsigned int prev_interval;
struct thermal_zone_device *thz_dev;
struct thermal_cooling_device *cl_dev;
struct platform_device *acerhdf_dev;
module_param(kernelmode, uint, 0);
MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
module_param(interval, uint, 0600);
MODULE_PARM_DESC(interval, "Polling interval of temperature check");
module_param(fanon, uint, 0600);
MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
module_param(fanoff, uint, 0600);
MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
module_param(verbose, uint, 0600);
MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
module_param_string(force_bios, force_bios, 16, 0);
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
module_param_string(force_product, force_product, 16, 0);
MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
/*
* cmd_off: to switch the fan completely off
* chk_off: to check if the fan is off
* cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
* the fan speed depending on the temperature
*/
struct fancmd {
u8 cmd_off;
u8 chk_off;
u8 cmd_auto;
};
/* BIOS settings */
struct bios_settings_t {
const char *vendor;
const char *product;
const char *version;
unsigned char fanreg;
unsigned char tempreg;
struct fancmd cmd;
};
/* Register addresses and values for different BIOS versions */
static const struct bios_settings_t bios_tbl[] = {
/* AOA110 */
{"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x1f, 0x00} },
{"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x1f, 0x00} },
{"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
{"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
{"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
{"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
{"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x21, 0x00} },
{"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x21, 0x00} },
{"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x21, 0x00} },
/* AOA150 */
{"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x20, 0x00} },
/* Acer 1410 */
{"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} },
/* special BIOS / other */
{"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x21, 0x00} },
{"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Gateway ", "LT31 ", "v1.3103 ", 0x55, 0x58,
{0x10, 0x0f, 0x00} },
{"Gateway ", "LT31 ", "v1.3201 ", 0x55, 0x58,
{0x10, 0x0f, 0x00} },
{"Gateway ", "LT31 ", "v1.3302 ", 0x55, 0x58,
{0x10, 0x0f, 0x00} },
{"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x21, 0x00} },
{"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} },
{"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x21, 0x00} },
{"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} },
/* pewpew-terminator */
{"", "", "", 0, 0, {0, 0, 0} }
};
static const struct bios_settings_t *bios_cfg __read_mostly;
static int acerhdf_get_temp(int *temp)
{
u8 read_temp;
if (ec_read(bios_cfg->tempreg, &read_temp))
return -EINVAL;
*temp = read_temp * 1000;
return 0;
}
static int acerhdf_get_fanstate(int *state)
{
u8 fan;
if (ec_read(bios_cfg->fanreg, &fan))
return -EINVAL;
if (fan != bios_cfg->cmd.chk_off)
*state = ACERHDF_FAN_AUTO;
else
*state = ACERHDF_FAN_OFF;
return 0;
}
static void acerhdf_change_fanstate(int state)
{
unsigned char cmd;
if (verbose)
pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
"OFF" : "ON");
if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
pr_err("invalid fan state %d requested, setting to auto!\n",
state);
state = ACERHDF_FAN_AUTO;
}
cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
: bios_cfg->cmd.cmd_auto;
fanstate = state;
ec_write(bios_cfg->fanreg, cmd);
}
static void acerhdf_check_param(struct thermal_zone_device *thermal)
{
if (fanon > ACERHDF_MAX_FANON) {
pr_err("fanon temperature too high, set to %d\n",
ACERHDF_MAX_FANON);
fanon = ACERHDF_MAX_FANON;
}
if (kernelmode && prev_interval != interval) {
if (interval > ACERHDF_MAX_INTERVAL) {
pr_err("interval too high, set to %d\n",
ACERHDF_MAX_INTERVAL);
interval = ACERHDF_MAX_INTERVAL;
}
if (verbose)
pr_notice("interval changed to: %d\n",
interval);
thermal->polling_delay = interval*1000;
prev_interval = interval;
}
}
/*
* This is the thermal zone callback which does the delayed polling of the fan
* state. We do check /sysfs-originating settings here in acerhdf_check_param()
* as late as the polling interval is since we can't do that in the respective
* accessors of the module parameters.
*/
static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
unsigned long *t)
{
int temp, err = 0;
acerhdf_check_param(thermal);
err = acerhdf_get_temp(&temp);
if (err)
return err;
if (verbose)
pr_notice("temp %d\n", temp);
*t = temp;
return 0;
}
static int acerhdf_bind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
/* if the cooling device is the one from acerhdf bind it */
if (cdev != cl_dev)
return 0;
if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
pr_err("error binding cooling dev\n");
return -EINVAL;
}
return 0;
}
static int acerhdf_unbind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
if (cdev != cl_dev)
return 0;
if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
pr_err("error unbinding cooling dev\n");
return -EINVAL;
}
return 0;
}
static inline void acerhdf_revert_to_bios_mode(void)
{
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
kernelmode = 0;
if (thz_dev)
thz_dev->polling_delay = 0;
pr_notice("kernel mode fan control OFF\n");
}
static inline void acerhdf_enable_kernelmode(void)
{
kernelmode = 1;
thz_dev->polling_delay = interval*1000;
thermal_zone_device_update(thz_dev);
pr_notice("kernel mode fan control ON\n");
}
static int acerhdf_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
if (verbose)
pr_notice("kernel mode fan control %d\n", kernelmode);
*mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
: THERMAL_DEVICE_DISABLED;
return 0;
}
/*
* set operation mode;
* enabled: the thermal layer of the kernel takes care about
* the temperature and the fan.
* disabled: the BIOS takes control of the fan.
*/
static int acerhdf_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
{
if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
acerhdf_revert_to_bios_mode();
else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode)
acerhdf_enable_kernelmode();
return 0;
}
static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
enum thermal_trip_type *type)
{
if (trip == 0)
*type = THERMAL_TRIP_ACTIVE;
return 0;
}
static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
unsigned long *temp)
{
if (trip == 0)
*temp = fanon;
return 0;
}
static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
unsigned long *temperature)
{
*temperature = ACERHDF_TEMP_CRIT;
return 0;
}
/* bind callback functions to thermalzone */
struct thermal_zone_device_ops acerhdf_dev_ops = {
.bind = acerhdf_bind,
.unbind = acerhdf_unbind,
.get_temp = acerhdf_get_ec_temp,
.get_mode = acerhdf_get_mode,
.set_mode = acerhdf_set_mode,
.get_trip_type = acerhdf_get_trip_type,
.get_trip_temp = acerhdf_get_trip_temp,
.get_crit_temp = acerhdf_get_crit_temp,
};
/*
* cooling device callback functions
* get maximal fan cooling state
*/
static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = 1;
return 0;
}
static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
int err = 0, tmp;
err = acerhdf_get_fanstate(&tmp);
if (err)
return err;
*state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0;
return 0;
}
/* change current fan state - is overwritten when running in kernel mode */
static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
int cur_temp, cur_state, err = 0;
if (!kernelmode)
return 0;
err = acerhdf_get_temp(&cur_temp);
if (err) {
pr_err("error reading temperature, hand off control to BIOS\n");
goto err_out;
}
err = acerhdf_get_fanstate(&cur_state);
if (err) {
pr_err("error reading fan state, hand off control to BIOS\n");
goto err_out;
}
if (state == 0) {
/* turn fan off only if below fanoff temperature */
if ((cur_state == ACERHDF_FAN_AUTO) &&
(cur_temp < fanoff))
acerhdf_change_fanstate(ACERHDF_FAN_OFF);
} else {
if (cur_state == ACERHDF_FAN_OFF)
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
}
return 0;
err_out:
acerhdf_revert_to_bios_mode();
return -EINVAL;
}
/* bind fan callbacks to fan device */
struct thermal_cooling_device_ops acerhdf_cooling_ops = {
.get_max_state = acerhdf_get_max_state,
.get_cur_state = acerhdf_get_cur_state,
.set_cur_state = acerhdf_set_cur_state,
};
/* suspend / resume functionality */
static int acerhdf_suspend(struct device *dev)
{
if (kernelmode)
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
if (verbose)
pr_notice("going suspend\n");
return 0;
}
static int __devinit acerhdf_probe(struct platform_device *device)
{
return 0;
}
static int acerhdf_remove(struct platform_device *device)
{
return 0;
}
static struct dev_pm_ops acerhdf_pm_ops = {
.suspend = acerhdf_suspend,
.freeze = acerhdf_suspend,
};
static struct platform_driver acerhdf_driver = {
.driver = {
.name = "acerhdf",
.owner = THIS_MODULE,
.pm = &acerhdf_pm_ops,
},
.probe = acerhdf_probe,
.remove = acerhdf_remove,
};
/* check hardware */
static int acerhdf_check_hardware(void)
{
char const *vendor, *version, *product;
int i;
unsigned long prod_len = 0;
/* get BIOS data */
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
version = dmi_get_system_info(DMI_BIOS_VERSION);
product = dmi_get_system_info(DMI_PRODUCT_NAME);
pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
if (force_bios[0]) {
version = force_bios;
pr_info("forcing BIOS version: %s\n", version);
kernelmode = 0;
}
if (force_product[0]) {
product = force_product;
pr_info("forcing BIOS product: %s\n", product);
kernelmode = 0;
}
prod_len = strlen(product);
if (verbose)
pr_info("BIOS info: %s %s, product: %s\n",
vendor, version, product);
/* search BIOS version and vendor in BIOS settings table */
for (i = 0; bios_tbl[i].version[0]; i++) {
if (strlen(bios_tbl[i].product) >= prod_len &&
!strncmp(bios_tbl[i].product, product,
strlen(bios_tbl[i].product)) &&
!strcmp(bios_tbl[i].vendor, vendor) &&
!strcmp(bios_tbl[i].version, version)) {
bios_cfg = &bios_tbl[i];
break;
}
}
if (!bios_cfg) {
pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
"please report, aborting!\n", vendor, product, version);
return -EINVAL;
}
/*
* if started with kernel mode off, prevent the kernel from switching
* off the fan
*/
if (!kernelmode) {
pr_notice("Fan control off, to enable do:\n");
pr_notice("echo -n \"enabled\" > "
"/sys/class/thermal/thermal_zone0/mode\n");
}
return 0;
}
static int acerhdf_register_platform(void)
{
int err = 0;
err = platform_driver_register(&acerhdf_driver);
if (err)
return err;
acerhdf_dev = platform_device_alloc("acerhdf", -1);
platform_device_add(acerhdf_dev);
return 0;
}
static void acerhdf_unregister_platform(void)
{
if (!acerhdf_dev)
return;
platform_device_del(acerhdf_dev);
platform_driver_unregister(&acerhdf_driver);
}
static int acerhdf_register_thermal(void)
{
cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
&acerhdf_cooling_ops);
if (IS_ERR(cl_dev))
return -EINVAL;
thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
&acerhdf_dev_ops, 0, 0, 0,
(kernelmode) ? interval*1000 : 0);
if (IS_ERR(thz_dev))
return -EINVAL;
return 0;
}
static void acerhdf_unregister_thermal(void)
{
if (cl_dev) {
thermal_cooling_device_unregister(cl_dev);
cl_dev = NULL;
}
if (thz_dev) {
thermal_zone_device_unregister(thz_dev);
thz_dev = NULL;
}
}
static int __init acerhdf_init(void)
{
int err = 0;
err = acerhdf_check_hardware();
if (err)
goto out_err;
err = acerhdf_register_platform();
if (err)
goto err_unreg;
err = acerhdf_register_thermal();
if (err)
goto err_unreg;
return 0;
err_unreg:
acerhdf_unregister_thermal();
acerhdf_unregister_platform();
out_err:
return -ENODEV;
}
static void __exit acerhdf_exit(void)
{
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
acerhdf_unregister_thermal();
acerhdf_unregister_platform();
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter Feuerer");
MODULE_DESCRIPTION("Aspire One temperature and fan driver");
MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:");
MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:");
module_init(acerhdf_init);
module_exit(acerhdf_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,414 @@
/*
* Driver for Dell laptop extras
*
* Copyright (c) Red Hat <mjg@redhat.com>
*
* Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
* Inc.
*
* 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
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/rfkill.h>
#include <linux/power_supply.h>
#include <linux/acpi.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
/* This structure will be modified by the firmware when we enter
* system management mode, hence the volatiles */
struct calling_interface_buffer {
u16 class;
u16 select;
volatile u32 input[4];
volatile u32 output[4];
} __packed;
struct calling_interface_token {
u16 tokenID;
u16 location;
union {
u16 value;
u16 stringlength;
};
};
struct calling_interface_structure {
struct dmi_header header;
u16 cmdIOAddress;
u8 cmdIOCode;
u32 supportedCmds;
struct calling_interface_token tokens[];
} __packed;
static int da_command_address;
static int da_command_code;
static int da_num_tokens;
static struct calling_interface_token *da_tokens;
static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
static const struct dmi_system_id __initdata dell_device_table[] = {
{
.ident = "Dell laptop",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
},
},
{
.ident = "Dell Computer Corporation",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
},
},
{ }
};
static void parse_da_table(const struct dmi_header *dm)
{
/* Final token is a terminator, so we don't want to copy it */
int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
struct calling_interface_structure *table =
container_of(dm, struct calling_interface_structure, header);
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
6 bytes of entry */
if (dm->length < 17)
return;
da_command_address = table->cmdIOAddress;
da_command_code = table->cmdIOCode;
da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
sizeof(struct calling_interface_token),
GFP_KERNEL);
if (!da_tokens)
return;
memcpy(da_tokens+da_num_tokens, table->tokens,
sizeof(struct calling_interface_token) * tokens);
da_num_tokens += tokens;
}
static void find_tokens(const struct dmi_header *dm, void *dummy)
{
switch (dm->type) {
case 0xd4: /* Indexed IO */
break;
case 0xd5: /* Protected Area Type 1 */
break;
case 0xd6: /* Protected Area Type 2 */
break;
case 0xda: /* Calling interface */
parse_da_table(dm);
break;
}
}
static int find_token_location(int tokenid)
{
int i;
for (i = 0; i < da_num_tokens; i++) {
if (da_tokens[i].tokenID == tokenid)
return da_tokens[i].location;
}
return -1;
}
static struct calling_interface_buffer *
dell_send_request(struct calling_interface_buffer *buffer, int class,
int select)
{
struct smi_cmd command;
command.magic = SMI_CMD_MAGIC;
command.command_address = da_command_address;
command.command_code = da_command_code;
command.ebx = virt_to_phys(buffer);
command.ecx = 0x42534931;
buffer->class = class;
buffer->select = select;
dcdbas_smi_request(&command);
return buffer;
}
/* Derived from information in DellWirelessCtl.cpp:
Class 17, select 11 is radio control. It returns an array of 32-bit values.
result[0]: return code
result[1]:
Bit 0: Hardware switch supported
Bit 1: Wifi locator supported
Bit 2: Wifi is supported
Bit 3: Bluetooth is supported
Bit 4: WWAN is supported
Bit 5: Wireless keyboard supported
Bits 6-7: Reserved
Bit 8: Wifi is installed
Bit 9: Bluetooth is installed
Bit 10: WWAN is installed
Bits 11-15: Reserved
Bit 16: Hardware switch is on
Bit 17: Wifi is blocked
Bit 18: Bluetooth is blocked
Bit 19: WWAN is blocked
Bits 20-31: Reserved
result[2]: NVRAM size in bytes
result[3]: NVRAM format version number
*/
static int dell_rfkill_set(void *data, bool blocked)
{
struct calling_interface_buffer buffer;
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = (1 | (radio<<8) | (disable << 16));
dell_send_request(&buffer, 17, 11);
return 0;
}
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
struct calling_interface_buffer buffer;
int status;
int bit = (unsigned long)data + 16;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];
if (status & BIT(bit))
rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
}
static const struct rfkill_ops dell_rfkill_ops = {
.set_block = dell_rfkill_set,
.query = dell_rfkill_query,
};
static int dell_setup_rfkill(void)
{
struct calling_interface_buffer buffer;
int status;
int ret;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
goto err_wifi;
}
ret = rfkill_register(wifi_rfkill);
if (ret)
goto err_wifi;
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
ret = -ENOMEM;
goto err_bluetooth;
}
ret = rfkill_register(bluetooth_rfkill);
if (ret)
goto err_bluetooth;
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
goto err_wwan;
}
ret = rfkill_register(wwan_rfkill);
if (ret)
goto err_wwan;
}
return 0;
err_wwan:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
err_bluetooth:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
err_wifi:
rfkill_destroy(wifi_rfkill);
return ret;
}
static int dell_send_intensity(struct backlight_device *bd)
{
struct calling_interface_buffer buffer;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
buffer.input[1] = bd->props.brightness;
if (buffer.input[0] == -1)
return -ENODEV;
if (power_supply_is_system_supplied() > 0)
dell_send_request(&buffer, 1, 2);
else
dell_send_request(&buffer, 1, 1);
return 0;
}
static int dell_get_intensity(struct backlight_device *bd)
{
struct calling_interface_buffer buffer;
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
if (buffer.input[0] == -1)
return -ENODEV;
if (power_supply_is_system_supplied() > 0)
dell_send_request(&buffer, 0, 2);
else
dell_send_request(&buffer, 0, 1);
return buffer.output[1];
}
static struct backlight_ops dell_ops = {
.get_brightness = dell_get_intensity,
.update_status = dell_send_intensity,
};
static int __init dell_init(void)
{
struct calling_interface_buffer buffer;
int max_intensity = 0;
int ret;
if (!dmi_check_system(dell_device_table))
return -ENODEV;
dmi_walk(find_tokens, NULL);
if (!da_tokens) {
printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n");
return -ENODEV;
}
ret = dell_setup_rfkill();
if (ret) {
printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
goto out;
}
#ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't
* register the platform controller.
*/
if (acpi_video_backlight_support())
return 0;
#endif
memset(&buffer, 0, sizeof(struct calling_interface_buffer));
buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN);
if (buffer.input[0] != -1) {
dell_send_request(&buffer, 0, 2);
max_intensity = buffer.output[3];
}
if (max_intensity) {
dell_backlight_device = backlight_device_register(
"dell_backlight",
NULL, NULL,
&dell_ops);
if (IS_ERR(dell_backlight_device)) {
ret = PTR_ERR(dell_backlight_device);
dell_backlight_device = NULL;
goto out;
}
dell_backlight_device->props.max_brightness = max_intensity;
dell_backlight_device->props.brightness =
dell_get_intensity(dell_backlight_device);
backlight_update_status(dell_backlight_device);
}
return 0;
out:
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
if (wwan_rfkill)
rfkill_unregister(wwan_rfkill);
kfree(da_tokens);
return ret;
}
static void __exit dell_exit(void)
{
backlight_device_unregister(dell_backlight_device);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
if (wwan_rfkill)
rfkill_unregister(wwan_rfkill);
}
module_init(dell_init);
module_exit(dell_exit);
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");

View File

@@ -0,0 +1,264 @@
/*
* Dell 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/acpi.h>
#include <linux/string.h>
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
struct key_entry {
char type; /* See KE_* below */
u16 code;
u16 keycode;
};
enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
/*
* Certain keys are flagged as KE_IGNORE. All of these are either
* notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again.
*/
static struct key_entry dell_wmi_keymap[] = {
{KE_KEY, 0xe045, KEY_PROG1},
{KE_KEY, 0xe009, KEY_EJECTCD},
/* These also contain the brightness level at offset 6 */
{KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
/* Battery health status button */
{KE_KEY, 0xe007, KEY_BATTERY},
/* This is actually for all radios. Although physically a
* switch, the notification does not provide an indication of
* state and so it should be reported as a key */
{KE_KEY, 0xe008, KEY_WLAN},
/* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */
{KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
/* BIOS error detected */
{KE_IGNORE, 0xe00d, KEY_RESERVED},
/* Wifi Catcher */
{KE_KEY, 0xe011, KEY_PROG2},
/* Ambient light sensor toggle */
{KE_IGNORE, 0xe013, KEY_RESERVED},
{KE_IGNORE, 0xe020, KEY_MUTE},
{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
{KE_IGNORE, 0xe030, KEY_VOLUMEUP},
{KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
{KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
{KE_IGNORE, 0xe045, KEY_NUMLOCK},
{KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
{KE_END, 0}
};
static struct input_dev *dell_wmi_input_dev;
static struct key_entry *dell_wmi_get_entry_by_scancode(int code)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (code == key->code)
return key;
return NULL;
}
static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode)
{
struct key_entry *key;
for (key = dell_wmi_keymap; key->type != KE_END; key++)
if (key->type == KE_KEY && keycode == key->keycode)
return key;
return NULL;
}
static int dell_wmi_getkeycode(struct input_dev *dev, int scancode,
int *keycode)
{
struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
*keycode = key->keycode;
return 0;
}
return -EINVAL;
}
static int dell_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
struct key_entry *key;
int old_keycode;
if (keycode < 0 || keycode > KEY_MAX)
return -EINVAL;
key = dell_wmi_get_entry_by_scancode(scancode);
if (key && key->type == KE_KEY) {
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!dell_wmi_get_entry_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
return -EINVAL;
}
static void dell_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
printk(KERN_INFO "dell-wmi: bad event status 0x%x\n", status);
return;
}
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER) {
int *buffer = (int *)obj->buffer.pointer;
/*
* The upper bytes of the event may contain
* additional information, so mask them off for the
* scancode lookup
*/
key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF);
if (key) {
input_report_key(dell_wmi_input_dev, key->keycode, 1);
input_sync(dell_wmi_input_dev);
input_report_key(dell_wmi_input_dev, key->keycode, 0);
input_sync(dell_wmi_input_dev);
} else if (buffer[1] & 0xFFFF)
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
buffer[1] & 0xFFFF);
}
kfree(obj);
}
static int __init dell_wmi_input_setup(void)
{
struct key_entry *key;
int err;
dell_wmi_input_dev = input_allocate_device();
if (!dell_wmi_input_dev)
return -ENOMEM;
dell_wmi_input_dev->name = "Dell WMI hotkeys";
dell_wmi_input_dev->phys = "wmi/input0";
dell_wmi_input_dev->id.bustype = BUS_HOST;
dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode;
dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode;
for (key = dell_wmi_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
set_bit(EV_KEY, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->keybit);
break;
case KE_SW:
set_bit(EV_SW, dell_wmi_input_dev->evbit);
set_bit(key->keycode, dell_wmi_input_dev->swbit);
break;
}
}
err = input_register_device(dell_wmi_input_dev);
if (err) {
input_free_device(dell_wmi_input_dev);
return err;
}
return 0;
}
static int __init dell_wmi_init(void)
{
int err;
if (wmi_has_guid(DELL_EVENT_GUID)) {
err = dell_wmi_input_setup();
if (err)
return err;
err = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL);
if (err) {
input_unregister_device(dell_wmi_input_dev);
printk(KERN_ERR "dell-wmi: Unable to register"
" notify handler - %d\n", err);
return err;
}
} else
printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n");
return 0;
}
static void __exit dell_wmi_exit(void)
{
if (wmi_has_guid(DELL_EVENT_GUID)) {
wmi_remove_notify_handler(DELL_EVENT_GUID);
input_unregister_device(dell_wmi_input_dev);
}
}
module_init(dell_wmi_init);
module_exit(dell_wmi_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,585 @@
/*
* 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_HARDWARE_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);
static int hp_wmi_resume_handler(struct 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_END };
static struct key_entry hp_wmi_keymap[] = {
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
{KE_KEY, 0x20e6, KEY_PROG1},
{KE_KEY, 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 dev_pm_ops hp_wmi_pm_ops = {
.resume = hp_wmi_resume_handler,
.restore = hp_wmi_resume_handler,
};
static struct platform_driver hp_wmi_driver = {
.driver = {
.name = "hp-wmi",
.owner = THIS_MODULE,
.pm = &hp_wmi_pm_ops,
},
.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)
{
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
if (ret < 0)
return ret;
return ret & 0x1;
}
static int hp_wmi_tablet_state(void)
{
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
if (ret < 0)
return ret;
return (ret & 0x4) ? 1 : 0;
}
static int hp_wmi_set_block(void *data, bool blocked)
{
unsigned long b = (unsigned long) data;
int query = BIT(b + 8) | ((!blocked) << b);
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
}
static const struct rfkill_ops hp_wmi_rfkill_ops = {
.set_block = hp_wmi_set_block,
};
static bool hp_wmi_wifi_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x100)
return false;
else
return true;
}
static bool hp_wmi_bluetooth_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x10000)
return false;
else
return true;
}
static bool hp_wmi_wwan_state(void)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
if (wireless & 0x1000000)
return false;
else
return true;
}
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 show_tablet(struct device *dev, struct device_attribute *attr,
char *buf)
{
int value = hp_wmi_tablet_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 DEVICE_ATTR(tablet, S_IRUGO, show_tablet, 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;
acpi_status status;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status);
return;
}
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;
}
} else if (eventcode == 0x1) {
input_report_switch(hp_wmi_input_dev, SW_DOCK,
hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
} else if (eventcode == 0x5) {
if (wifi_rfkill)
rfkill_set_sw_state(wifi_rfkill,
hp_wmi_wifi_state());
if (bluetooth_rfkill)
rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
if (wwan_rfkill)
rfkill_set_sw_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");
kfree(obj);
}
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;
}
}
set_bit(EV_SW, hp_wmi_input_dev->evbit);
set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
/* Set initial hardware state */
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
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);
device_remove_file(&device->dev, &dev_attr_tablet);
}
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;
err = device_create_file(&device->dev, &dev_attr_tablet);
if (err)
goto add_sysfs_error;
if (wireless & 0x1) {
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN,
&hp_wmi_rfkill_ops,
(void *) 0);
err = rfkill_register(wifi_rfkill);
if (err)
goto register_wifi_error;
}
if (wireless & 0x2) {
bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
RFKILL_TYPE_BLUETOOTH,
&hp_wmi_rfkill_ops,
(void *) 1);
err = rfkill_register(bluetooth_rfkill);
if (err)
goto register_bluetooth_error;
}
if (wireless & 0x4) {
wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
RFKILL_TYPE_WWAN,
&hp_wmi_rfkill_ops,
(void *) 2);
err = rfkill_register(wwan_rfkill);
if (err)
goto register_wwan_err;
}
return 0;
register_wwan_err:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
register_wifi_error:
rfkill_destroy(wifi_rfkill);
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);
rfkill_destroy(wifi_rfkill);
}
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
}
if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
return 0;
}
static int hp_wmi_resume_handler(struct device *device)
{
/*
* Hardware state may have changed while suspended, so trigger
* input events for the current state. As this is a switch,
* the input layer will only actually pass it on if the state
* changed.
*/
if (hp_wmi_input_dev) {
input_report_switch(hp_wmi_input_dev, SW_DOCK,
hp_wmi_dock_state());
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
}
return 0;
}
static int __init hp_wmi_init(void)
{
int err;
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
hp_wmi_notify, NULL);
if (!err)
hp_wmi_input_setup();
}
if (wmi_has_guid(HPWMI_BIOS_GUID)) {
err = platform_driver_register(&hp_wmi_driver);
if (err)
return 0;
hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
if (!hp_wmi_platform_dev) {
platform_driver_unregister(&hp_wmi_driver);
return 0;
}
platform_device_add(hp_wmi_platform_dev);
}
return 0;
}
static void __exit hp_wmi_exit(void)
{
if (wmi_has_guid(HPWMI_EVENT_GUID)) {
wmi_remove_notify_handler(HPWMI_EVENT_GUID);
input_unregister_device(hp_wmi_input_dev);
}
if (hp_wmi_platform_dev) {
platform_device_del(hp_wmi_platform_dev);
platform_driver_unregister(&hp_wmi_driver);
}
}
module_init(hp_wmi_init);
module_exit(hp_wmi_exit);

View File

@@ -0,0 +1,527 @@
/*
* 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_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_cur_bandwidth(struct thermal_cooling_device *cdev,
unsigned long *value)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
unsigned long long result;
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, &result);
if (ACPI_FAILURE(status))
return -EFAULT;
*value = result;
return 0;
}
static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
unsigned long 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_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 %ld: status is %d\n", state, status);
if (ACPI_FAILURE(status))
return -EFAULT;
return 0;
}
static struct thermal_cooling_device_ops memory_cooling_ops = {
.get_max_state = memory_get_max_bandwidth,
.get_cur_state = memory_get_cur_bandwidth,
.set_cur_state = memory_set_cur_bandwidth,
};
/*
* Memory Device Management
*/
static int intel_menlow_memory_add(struct acpi_device *device)
{
int result = -ENODEV;
acpi_status status = AE_OK;
acpi_handle dummy;
struct thermal_cooling_device *cdev;
if (!device)
return -EINVAL;
status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
if (ACPI_FAILURE(status))
goto end;
status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
if (ACPI_FAILURE(status))
goto end;
cdev = thermal_cooling_device_register("Memory controller", device,
&memory_cooling_ops);
if (IS_ERR(cdev)) {
result = PTR_ERR(cdev);
goto end;
}
device->driver_data = cdev;
result = sysfs_create_link(&device->dev.kobj,
&cdev->device.kobj, "thermal_cooling");
if (result)
goto unregister;
result = sysfs_create_link(&cdev->device.kobj,
&device->dev.kobj, "device");
if (result) {
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
goto unregister;
}
end:
return result;
unregister:
thermal_cooling_device_unregister(cdev);
return result;
}
static int intel_menlow_memory_remove(struct acpi_device *device, int type)
{
struct thermal_cooling_device *cdev = acpi_driver_data(device);
if (!device || !cdev)
return -EINVAL;
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
sysfs_remove_link(&cdev->device.kobj, "device");
thermal_cooling_device_unregister(cdev);
return 0;
}
static const struct acpi_device_id intel_menlow_memory_ids[] = {
{"INT0002", 0},
{"", 0},
};
static struct acpi_driver intel_menlow_memory_driver = {
.name = "intel_menlow_thermal_control",
.ids = intel_menlow_memory_ids,
.ops = {
.add = intel_menlow_memory_add,
.remove = intel_menlow_memory_remove,
},
};
/*
* Sensor control on menlow platform
*/
#define THERMAL_AUX0 0
#define THERMAL_AUX1 1
#define GET_AUX0 "GAX0"
#define GET_AUX1 "GAX1"
#define SET_AUX0 "SAX0"
#define SET_AUX1 "SAX1"
struct intel_menlow_attribute {
struct device_attribute attr;
struct device *device;
acpi_handle handle;
struct list_head node;
};
static LIST_HEAD(intel_menlow_attr_list);
static DEFINE_MUTEX(intel_menlow_attr_lock);
/*
* sensor_get_auxtrip - get the current auxtrip value from sensor
* @name: Thermalzone name
* @auxtype : AUX0/AUX1
* @buf: syfs buffer
*/
static int sensor_get_auxtrip(acpi_handle handle, int index,
unsigned long long *value)
{
acpi_status status;
if ((index != 0 && index != 1) || !value)
return -EINVAL;
status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
NULL, value);
if (ACPI_FAILURE(status))
return -EIO;
return 0;
}
/*
* sensor_set_auxtrip - set the new auxtrip value to sensor
* @name: Thermalzone name
* @auxtype : AUX0/AUX1
* @buf: syfs buffer
*/
static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
{
acpi_status status;
union acpi_object arg = {
ACPI_TYPE_INTEGER
};
struct acpi_object_list args = {
1, &arg
};
unsigned long long temp;
if (index != 0 && index != 1)
return -EINVAL;
status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
NULL, &temp);
if (ACPI_FAILURE(status))
return -EIO;
if ((index && value < temp) || (!index && value > temp))
return -EINVAL;
arg.integer.value = value;
status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
&args, &temp);
if (ACPI_FAILURE(status))
return -EIO;
/* do we need to check the return value of SAX0/SAX1 ? */
return 0;
}
#define to_intel_menlow_attr(_attr) \
container_of(_attr, struct intel_menlow_attribute, attr)
static ssize_t aux0_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
unsigned long long value;
int result;
result = sensor_get_auxtrip(attr->handle, 0, &value);
return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
}
static ssize_t aux1_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
unsigned long long value;
int result;
result = sensor_get_auxtrip(attr->handle, 1, &value);
return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
}
static ssize_t aux0_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
int value;
int result;
/*Sanity check; should be a positive integer */
if (!sscanf(buf, "%d", &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
return result ? result : count;
}
static ssize_t aux1_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
int value;
int result;
/*Sanity check; should be a positive integer */
if (!sscanf(buf, "%d", &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
return result ? result : count;
}
/* BIOS can enable/disable the thermal user application in dabney platform */
#define BIOS_ENABLED "\\_TZ.GSTS"
static ssize_t bios_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
acpi_status status;
unsigned long long bios_enabled;
status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
if (ACPI_FAILURE(status))
return -ENODEV;
return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
}
static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
void *store, struct device *dev,
acpi_handle handle)
{
struct intel_menlow_attribute *attr;
int result;
attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
if (!attr)
return -ENOMEM;
attr->attr.attr.name = name;
attr->attr.attr.mode = mode;
attr->attr.show = show;
attr->attr.store = store;
attr->device = dev;
attr->handle = handle;
result = device_create_file(dev, &attr->attr);
if (result)
return result;
mutex_lock(&intel_menlow_attr_lock);
list_add_tail(&attr->node, &intel_menlow_attr_list);
mutex_unlock(&intel_menlow_attr_lock);
return 0;
}
static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
void *context, void **rv)
{
acpi_status status;
acpi_handle dummy;
struct thermal_zone_device *thermal;
int result;
result = acpi_bus_get_private_data(handle, (void **)&thermal);
if (result)
return 0;
/* _TZ must have the AUX0/1 methods */
status = acpi_get_handle(handle, GET_AUX0, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
status = acpi_get_handle(handle, SET_AUX0, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
result = intel_menlow_add_one_attribute("aux0", 0644,
aux0_show, aux0_store,
&thermal->device, handle);
if (result)
return AE_ERROR;
status = acpi_get_handle(handle, GET_AUX1, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
status = acpi_get_handle(handle, SET_AUX1, &dummy);
if (ACPI_FAILURE(status))
goto not_found;
result = intel_menlow_add_one_attribute("aux1", 0644,
aux1_show, aux1_store,
&thermal->device, handle);
if (result)
return AE_ERROR;
/*
* create the "dabney_enabled" attribute which means the user app
* should be loaded or not
*/
result = intel_menlow_add_one_attribute("bios_enabled", 0444,
bios_enabled_show, NULL,
&thermal->device, handle);
if (result)
return AE_ERROR;
not_found:
if (status == AE_NOT_FOUND)
return AE_OK;
else
return status;
}
static void intel_menlow_unregister_sensor(void)
{
struct intel_menlow_attribute *pos, *next;
mutex_lock(&intel_menlow_attr_lock);
list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
list_del(&pos->node);
device_remove_file(pos->device, &pos->attr);
kfree(pos);
}
mutex_unlock(&intel_menlow_attr_lock);
return;
}
static int __init intel_menlow_module_init(void)
{
int result = -ENODEV;
acpi_status status;
unsigned long long enable;
if (acpi_disabled)
return result;
/* Looking for the \_TZ.GSTS method */
status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
if (ACPI_FAILURE(status) || !enable)
return -ENODEV;
/* Looking for ACPI device MEM0 with hardware id INT0002 */
result = acpi_bus_register_driver(&intel_menlow_memory_driver);
if (result)
return result;
/* Looking for sensors in each ACPI thermal zone */
status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
intel_menlow_register_sensor, NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
return 0;
}
static void __exit intel_menlow_module_exit(void)
{
acpi_bus_unregister_driver(&intel_menlow_memory_driver);
intel_menlow_unregister_sensor();
}
module_init(intel_menlow_module_init);
module_exit(intel_menlow_module_exit);

View File

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

View File

@@ -0,0 +1,729 @@
/*
* 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 void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id pcc_device_ids[] = {
{ "MAT0012", 0},
{ "MAT0013", 0},
{ "MAT0018", 0},
{ "MAT0019", 0},
{ "", 0},
};
MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
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,
.notify = acpi_pcc_hotkey_notify,
},
};
#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;
status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
&params, NULL);
return status == AE_OK;
}
static inline int acpi_pcc_get_sqty(struct acpi_device *device)
{
unsigned long long s;
acpi_status status;
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;
status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, NULL,
&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 snprintf(buf, PAGE_SIZE, "%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 snprintf(buf, PAGE_SIZE, "%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 snprintf(buf, PAGE_SIZE, "%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 snprintf(buf, PAGE_SIZE, "%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;
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(struct acpi_device *device, u32 event)
{
struct pcc_acpi *pcc = acpi_driver_data(device);
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;
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;
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)
{
struct pcc_acpi *pcc;
int num_sifr, result;
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 backlight */
pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
&pcc_backlight_ops);
if (IS_ERR(pcc->backlight))
goto out_input;
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_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;
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);
if (!device || !pcc)
return -EINVAL;
sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
backlight_device_unregister(pcc->backlight);
input_unregister_device(pcc->input_dev);
/* no need to input_free_device() since core input API refcount and
* free()s the device */
kfree(pcc->sinf);
kfree(pcc);
return 0;
}
static void __exit acpi_pcc_exit(void)
{
acpi_bus_unregister_driver(&acpi_pcc_driver);
}
module_init(acpi_pcc_init);
module_exit(acpi_pcc_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
/*
* 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/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_INTEGER) {
tmp = obj->integer.value;
} 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) ? 0 : 1;
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_IRUGO | S_IWUSR, \
show_bool_##value, set_bool_##value);
show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
static void remove_fs(void)
{
device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
}
static int add_fs(void)
{
int ret;
ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
if (ret)
goto add_sysfs_error;
ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
if (ret)
goto add_sysfs_error;
return ret;
add_sysfs_error:
remove_fs();
return ret;
}
/* --------------------------------------------------------------------------
Driver Model
-------------------------------------------------------------------------- */
static int tc1100_probe(struct platform_device *device)
{
int result = 0;
result = add_fs();
return result;
}
static int tc1100_remove(struct platform_device *device)
{
remove_fs();
return 0;
}
static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
{
int ret;
ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
if (ret)
return ret;
ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
if (ret)
return ret;
return ret;
}
static int tc1100_resume(struct platform_device *dev)
{
int ret;
ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
if (ret)
return ret;
ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
if (ret)
return ret;
return ret;
}
static int __init tc1100_init(void)
{
int result = 0;
if (!wmi_has_guid(GUID))
return -ENODEV;
result = platform_driver_register(&tc1100_driver);
if (result)
return result;
tc1100_device = platform_device_alloc("tc1100-wmi", -1);
platform_device_add(tc1100_device);
printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
return result;
}
static void __exit tc1100_exit(void)
{
platform_device_del(tc1100_device);
platform_driver_unregister(&tc1100_driver);
printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
}
module_init(tc1100_init);
module_exit(tc1100_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,265 @@
/*
* ACPI driver for Topstar notebooks (hotkeys support only)
*
* Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
*
* Implementation inspired by existing x86 platform drivers, in special
* asus/eepc/fujitsu-laptop, thanks to their authors
*
* 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
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/input.h>
#define ACPI_TOPSTAR_CLASS "topstar"
struct topstar_hkey {
struct input_dev *inputdev;
};
struct tps_key_entry {
u8 code;
u16 keycode;
};
static struct tps_key_entry topstar_keymap[] = {
{ 0x80, KEY_BRIGHTNESSUP },
{ 0x81, KEY_BRIGHTNESSDOWN },
{ 0x83, KEY_VOLUMEUP },
{ 0x84, KEY_VOLUMEDOWN },
{ 0x85, KEY_MUTE },
{ 0x86, KEY_SWITCHVIDEOMODE },
{ 0x87, KEY_F13 }, /* touchpad enable/disable key */
{ 0x88, KEY_WLAN },
{ 0x8a, KEY_WWW },
{ 0x8b, KEY_MAIL },
{ 0x8c, KEY_MEDIA },
{ 0x96, KEY_F14 }, /* G key? */
{ }
};
static struct tps_key_entry *tps_get_key_by_scancode(int code)
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++)
if (code == key->code)
return key;
return NULL;
}
static struct tps_key_entry *tps_get_key_by_keycode(int code)
{
struct tps_key_entry *key;
for (key = topstar_keymap; key->code; key++)
if (code == key->keycode)
return key;
return NULL;
}
static void acpi_topstar_notify(struct acpi_device *device, u32 event)
{
struct tps_key_entry *key;
static bool dup_evnt[2];
bool *dup;
struct topstar_hkey *hkey = acpi_driver_data(device);
/* 0x83 and 0x84 key events comes duplicated... */
if (event == 0x83 || event == 0x84) {
dup = &dup_evnt[event - 0x83];
if (*dup) {
*dup = false;
return;
}
*dup = true;
}
/*
* 'G key' generate two event codes, convert to only
* one event/key code for now (3G switch?)
*/
if (event == 0x97)
event = 0x96;
key = tps_get_key_by_scancode(event);
if (key) {
input_report_key(hkey->inputdev, key->keycode, 1);
input_sync(hkey->inputdev);
input_report_key(hkey->inputdev, key->keycode, 0);
input_sync(hkey->inputdev);
return;
}
/* Known non hotkey events don't handled or that we don't care yet */
if (event == 0x8e || event == 0x8f || event == 0x90)
return;
pr_info("unknown event = 0x%02x\n", event);
}
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
{
acpi_status status;
union acpi_object fncx_params[1] = {
{ .type = ACPI_TYPE_INTEGER }
};
struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] };
fncx_params[0].integer.value = state ? 0x86 : 0x87;
status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL);
if (ACPI_FAILURE(status)) {
pr_err("Unable to switch FNCX notifications\n");
return -ENODEV;
}
return 0;
}
static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode)
{
struct tps_key_entry *key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
*keycode = key->keycode;
return 0;
}
static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
struct tps_key_entry *key;
int old_keycode;
if (keycode < 0 || keycode > KEY_MAX)
return -EINVAL;
key = tps_get_key_by_scancode(scancode);
if (!key)
return -EINVAL;
old_keycode = key->keycode;
key->keycode = keycode;
set_bit(keycode, dev->keybit);
if (!tps_get_key_by_keycode(old_keycode))
clear_bit(old_keycode, dev->keybit);
return 0;
}
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
{
struct tps_key_entry *key;
hkey->inputdev = input_allocate_device();
if (!hkey->inputdev) {
pr_err("Unable to allocate input device\n");
return -ENODEV;
}
hkey->inputdev->name = "Topstar Laptop extra buttons";
hkey->inputdev->phys = "topstar/input0";
hkey->inputdev->id.bustype = BUS_HOST;
hkey->inputdev->getkeycode = topstar_getkeycode;
hkey->inputdev->setkeycode = topstar_setkeycode;
for (key = topstar_keymap; key->code; key++) {
set_bit(EV_KEY, hkey->inputdev->evbit);
set_bit(key->keycode, hkey->inputdev->keybit);
}
if (input_register_device(hkey->inputdev)) {
pr_err("Unable to register input device\n");
input_free_device(hkey->inputdev);
return -ENODEV;
}
return 0;
}
static int acpi_topstar_add(struct acpi_device *device)
{
struct topstar_hkey *tps_hkey;
tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
if (!tps_hkey)
return -ENOMEM;
strcpy(acpi_device_name(device), "Topstar TPSACPI");
strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
if (acpi_topstar_fncx_switch(device, true))
goto add_err;
if (acpi_topstar_init_hkey(tps_hkey))
goto add_err;
device->driver_data = tps_hkey;
return 0;
add_err:
kfree(tps_hkey);
return -ENODEV;
}
static int acpi_topstar_remove(struct acpi_device *device, int type)
{
struct topstar_hkey *tps_hkey = acpi_driver_data(device);
acpi_topstar_fncx_switch(device, false);
input_unregister_device(tps_hkey->inputdev);
kfree(tps_hkey);
return 0;
}
static const struct acpi_device_id topstar_device_ids[] = {
{ "TPSACPI01", 0 },
{ "", 0 },
};
MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
static struct acpi_driver acpi_topstar_driver = {
.name = "Topstar laptop ACPI driver",
.class = ACPI_TOPSTAR_CLASS,
.ids = topstar_device_ids,
.ops = {
.add = acpi_topstar_add,
.remove = acpi_topstar_remove,
.notify = acpi_topstar_notify,
},
};
static int __init topstar_laptop_init(void)
{
int ret;
ret = acpi_bus_register_driver(&acpi_topstar_driver);
if (ret < 0)
return ret;
printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n");
return 0;
}
static void __exit topstar_laptop_exit(void)
{
acpi_bus_unregister_driver(&acpi_topstar_driver);
}
module_init(topstar_laptop_init);
module_exit(topstar_laptop_exit);
MODULE_AUTHOR("Herton Ronaldo Krzesinski");
MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,791 @@
/*
* toshiba_acpi.c - Toshiba Laptop ACPI Extras
*
*
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
*
* 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
*
*
* The devolpment page for this driver is located at
* http://memebeam.org/toys/ToshibaAcpiDriver.
*
* Credits:
* Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
* engineering the Windows drivers
* Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
* Rob Miller - TV out and hotkeys help
*
*
* TODO
*
*/
#define TOSHIBA_ACPI_VERSION "0.19"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
#define MY_LOGPREFIX "toshiba_acpi: "
#define MY_ERR KERN_ERR MY_LOGPREFIX
#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
#define MY_INFO KERN_INFO MY_LOGPREFIX
/* Toshiba ACPI method paths */
#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
/* Toshiba HCI interface definitions
*
* HCI is Toshiba's "Hardware Control Interface" which is supposed to
* be uniform across all their models. Ideally we would just call
* dedicated ACPI methods instead of using this primitive interface.
* However the ACPI methods seem to be incomplete in some areas (for
* example they allow setting, but not reading, the LCD brightness value),
* so this is still useful.
*/
#define HCI_WORDS 6
/* operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
/* return codes */
#define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
/* registers */
#define HCI_FAN 0x0004
#define HCI_SYSTEM_EVENT 0x0016
#define HCI_VIDEO_OUT 0x001c
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
/* field definitions */
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
#define HCI_WIRELESS_KILL_SWITCH 0x01
#define HCI_WIRELESS_BT_PRESENT 0x0f
#define HCI_WIRELESS_BT_ATTACH 0x40
#define HCI_WIRELESS_BT_POWER 0x80
static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6208", 0},
{"TOS1900", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
/* utility
*/
static __inline__ void _set_bit(u32 * word, u32 mask, int value)
{
*word = (*word & ~mask) | (mask * value);
}
/* acpi interface wrappers
*/
static int is_valid_acpi_path(const char *methodName)
{
acpi_handle handle;
acpi_status status;
status = acpi_get_handle(NULL, (char *)methodName, &handle);
return !ACPI_FAILURE(status);
}
static int write_acpi_int(const char *methodName, int val)
{
struct acpi_object_list params;
union acpi_object in_objs[1];
acpi_status status;
params.count = ARRAY_SIZE(in_objs);
params.pointer = in_objs;
in_objs[0].type = ACPI_TYPE_INTEGER;
in_objs[0].integer.value = val;
status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
return (status == AE_OK);
}
#if 0
static int read_acpi_int(const char *methodName, int *pVal)
{
struct acpi_buffer results;
union acpi_object out_objs[1];
acpi_status status;
results.length = sizeof(out_objs);
results.pointer = out_objs;
status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
*pVal = out_objs[0].integer.value;
return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
}
#endif
static const char *method_hci /*= 0*/ ;
/* Perform a raw HCI call. Here we don't care about input or output buffer
* format.
*/
static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
{
struct acpi_object_list params;
union acpi_object in_objs[HCI_WORDS];
struct acpi_buffer results;
union acpi_object out_objs[HCI_WORDS + 1];
acpi_status status;
int i;
params.count = HCI_WORDS;
params.pointer = in_objs;
for (i = 0; i < HCI_WORDS; ++i) {
in_objs[i].type = ACPI_TYPE_INTEGER;
in_objs[i].integer.value = in[i];
}
results.length = sizeof(out_objs);
results.pointer = out_objs;
status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
&results);
if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
for (i = 0; i < out_objs->package.count; ++i) {
out[i] = out_objs->package.elements[i].integer.value;
}
}
return status;
}
/* common hci tasks (get or set one or two value)
*
* In addition to the ACPI status, the HCI system returns a result which
* may be useful (such as "not supported").
*/
static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
{
u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status = hci_raw(in, out);
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
{
u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status = hci_raw(in, out);
*out1 = out[2];
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status = hci_raw(in, out);
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
{
u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status = hci_raw(in, out);
*out1 = out[2];
*out2 = out[3];
*result = (status == AE_OK) ? out[0] : HCI_FAILURE;
return status;
}
struct toshiba_acpi_dev {
struct platform_device *p_dev;
struct rfkill *bt_rfk;
const char *bt_name;
struct mutex mutex;
};
static struct toshiba_acpi_dev toshiba_acpi = {
.bt_name = "Toshiba Bluetooth",
};
/* Bluetooth rfkill handlers */
static u32 hci_get_bt_present(bool *present)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0;
hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
if (hci_result == HCI_SUCCESS)
*present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
return hci_result;
}
static u32 hci_get_radio_state(bool *radio_state)
{
u32 hci_result;
u32 value, value2;
value = 0;
value2 = 0x0001;
hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
*radio_state = value & HCI_WIRELESS_KILL_SWITCH;
return hci_result;
}
static int bt_rfkill_set_block(void *data, bool blocked)
{
struct toshiba_acpi_dev *dev = data;
u32 result1, result2;
u32 value;
int err;
bool radio_state;
value = (blocked == false);
mutex_lock(&dev->mutex);
if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) {
err = -EBUSY;
goto out;
}
if (!radio_state) {
err = 0;
goto out;
}
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
err = -EBUSY;
else
err = 0;
out:
mutex_unlock(&dev->mutex);
return err;
}
static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
{
bool new_rfk_state;
bool value;
u32 hci_result;
struct toshiba_acpi_dev *dev = data;
mutex_lock(&dev->mutex);
hci_result = hci_get_radio_state(&value);
if (hci_result != HCI_SUCCESS) {
/* Can't do anything useful */
mutex_unlock(&dev->mutex);
return;
}
new_rfk_state = value;
mutex_unlock(&dev->mutex);
if (rfkill_set_hw_state(rfkill, !new_rfk_state))
bt_rfkill_set_block(data, true);
}
static const struct rfkill_ops toshiba_rfk_ops = {
.set_block = bt_rfkill_set_block,
.poll = bt_rfkill_poll,
};
static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
static struct backlight_device *toshiba_backlight_device;
static int force_fan;
static int last_key_event;
static int key_event_valid;
typedef struct _ProcItem {
const char *name;
char *(*read_func) (char *);
unsigned long (*write_func) (const char *, unsigned long);
} ProcItem;
/* proc file handlers
*/
static int
dispatch_read(char *page, char **start, off_t off, int count, int *eof,
ProcItem * item)
{
char *p = page;
int len;
if (off == 0)
p = item->read_func(p);
/* ISSUE: I don't understand this code */
len = (p - page);
if (len <= off + count)
*eof = 1;
*start = page + off;
len -= off;
if (len > count)
len = count;
if (len < 0)
len = 0;
return len;
}
static int
dispatch_write(struct file *file, const char __user * buffer,
unsigned long count, ProcItem * item)
{
int result;
char *tmp_buffer;
/* Arg buffer points to userspace memory, which can't be accessed
* directly. Since we're making a copy, zero-terminate the
* destination so that sscanf can be used on it safely.
*/
tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
if (!tmp_buffer)
return -ENOMEM;
if (copy_from_user(tmp_buffer, buffer, count)) {
result = -EFAULT;
} else {
tmp_buffer[count] = 0;
result = item->write_func(tmp_buffer, count);
}
kfree(tmp_buffer);
return result;
}
static int get_lcd(struct backlight_device *bd)
{
u32 hci_result;
u32 value;
hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
} else
return -EFAULT;
}
static char *read_lcd(char *p)
{
int value = get_lcd(NULL);
if (value >= 0) {
p += sprintf(p, "brightness: %d\n", value);
p += sprintf(p, "brightness_levels: %d\n",
HCI_LCD_BRIGHTNESS_LEVELS);
} else {
printk(MY_ERR "Error reading LCD brightness\n");
}
return p;
}
static int set_lcd(int value)
{
u32 hci_result;
value = value << HCI_LCD_BRIGHTNESS_SHIFT;
hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
if (hci_result != HCI_SUCCESS)
return -EFAULT;
return 0;
}
static int set_lcd_status(struct backlight_device *bd)
{
return set_lcd(bd->props.brightness);
}
static unsigned long write_lcd(const char *buffer, unsigned long count)
{
int value;
int ret;
if (sscanf(buffer, " brightness : %i", &value) == 1 &&
value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
ret = set_lcd(value);
if (ret == 0)
ret = count;
} else {
ret = -EINVAL;
}
return ret;
}
static char *read_video(char *p)
{
u32 hci_result;
u32 value;
hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
p += sprintf(p, "lcd_out: %d\n", is_lcd);
p += sprintf(p, "crt_out: %d\n", is_crt);
p += sprintf(p, "tv_out: %d\n", is_tv);
} else {
printk(MY_ERR "Error reading video out status\n");
}
return p;
}
static unsigned long write_video(const char *buffer, unsigned long count)
{
int value;
int remain = count;
int lcd_out = -1;
int crt_out = -1;
int tv_out = -1;
u32 hci_result;
u32 video_out;
/* scan expression. Multiple expressions may be delimited with ;
*
* NOTE: to keep scanning simple, invalid fields are ignored
*/
while (remain) {
if (sscanf(buffer, " lcd_out : %i", &value) == 1)
lcd_out = value & 1;
else if (sscanf(buffer, " crt_out : %i", &value) == 1)
crt_out = value & 1;
else if (sscanf(buffer, " tv_out : %i", &value) == 1)
tv_out = value & 1;
/* advance to one character past the next ; */
do {
++buffer;
--remain;
}
while (remain && *(buffer - 1) != ';');
}
hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
if (hci_result == HCI_SUCCESS) {
unsigned int new_video_out = video_out;
if (lcd_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
if (crt_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
if (tv_out != -1)
_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
/* To avoid unnecessary video disruption, only write the new
* video setting if something changed. */
if (new_video_out != video_out)
write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
} else {
return -EFAULT;
}
return count;
}
static char *read_fan(char *p)
{
u32 hci_result;
u32 value;
hci_read1(HCI_FAN, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
p += sprintf(p, "running: %d\n", (value > 0));
p += sprintf(p, "force_on: %d\n", force_fan);
} else {
printk(MY_ERR "Error reading fan status\n");
}
return p;
}
static unsigned long write_fan(const char *buffer, unsigned long count)
{
int value;
u32 hci_result;
if (sscanf(buffer, " force_on : %i", &value) == 1 &&
value >= 0 && value <= 1) {
hci_write1(HCI_FAN, value, &hci_result);
if (hci_result != HCI_SUCCESS)
return -EFAULT;
else
force_fan = value;
} else {
return -EINVAL;
}
return count;
}
static char *read_keys(char *p)
{
u32 hci_result;
u32 value;
if (!key_event_valid) {
hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
if (hci_result == HCI_SUCCESS) {
key_event_valid = 1;
last_key_event = value;
} else if (hci_result == HCI_EMPTY) {
/* better luck next time */
} else if (hci_result == HCI_NOT_SUPPORTED) {
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
* become disabled. */
hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
printk(MY_NOTICE "Re-enabled hotkeys\n");
} else {
printk(MY_ERR "Error reading hotkey status\n");
goto end;
}
}
p += sprintf(p, "hotkey_ready: %d\n", key_event_valid);
p += sprintf(p, "hotkey: 0x%04x\n", last_key_event);
end:
return p;
}
static unsigned long write_keys(const char *buffer, unsigned long count)
{
int value;
if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) {
key_event_valid = 0;
} else {
return -EINVAL;
}
return count;
}
static char *read_version(char *p)
{
p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION);
p += sprintf(p, "proc_interface: %d\n",
PROC_INTERFACE_VERSION);
return p;
}
/* proc and module init
*/
#define PROC_TOSHIBA "toshiba"
static ProcItem proc_items[] = {
{"lcd", read_lcd, write_lcd},
{"video", read_video, write_video},
{"fan", read_fan, write_fan},
{"keys", read_keys, write_keys},
{"version", read_version, NULL},
{NULL}
};
static acpi_status __init add_device(void)
{
struct proc_dir_entry *proc;
ProcItem *item;
for (item = proc_items; item->name; ++item) {
proc = create_proc_read_entry(item->name,
S_IFREG | S_IRUGO | S_IWUSR,
toshiba_proc_dir,
(read_proc_t *) dispatch_read,
item);
if (proc && item->write_func)
proc->write_proc = (write_proc_t *) dispatch_write;
}
return AE_OK;
}
static acpi_status remove_device(void)
{
ProcItem *item;
for (item = proc_items; item->name; ++item)
remove_proc_entry(item->name, toshiba_proc_dir);
return AE_OK;
}
static struct backlight_ops toshiba_backlight_data = {
.get_brightness = get_lcd,
.update_status = set_lcd_status,
};
static void toshiba_acpi_exit(void)
{
if (toshiba_acpi.bt_rfk) {
rfkill_unregister(toshiba_acpi.bt_rfk);
rfkill_destroy(toshiba_acpi.bt_rfk);
}
if (toshiba_backlight_device)
backlight_device_unregister(toshiba_backlight_device);
remove_device();
if (toshiba_proc_dir)
remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
platform_device_unregister(toshiba_acpi.p_dev);
return;
}
static int __init toshiba_acpi_init(void)
{
acpi_status status = AE_OK;
u32 hci_result;
bool bt_present;
int ret = 0;
if (acpi_disabled)
return -ENODEV;
/* simple device detection: look for HCI method */
if (is_valid_acpi_path(METHOD_HCI_1))
method_hci = METHOD_HCI_1;
else if (is_valid_acpi_path(METHOD_HCI_2))
method_hci = METHOD_HCI_2;
else
return -ENODEV;
printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);
printk(MY_INFO " HCI method: %s\n", method_hci);
mutex_init(&toshiba_acpi.mutex);
toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
-1, NULL, 0);
if (IS_ERR(toshiba_acpi.p_dev)) {
ret = PTR_ERR(toshiba_acpi.p_dev);
printk(MY_ERR "unable to register platform device\n");
toshiba_acpi.p_dev = NULL;
toshiba_acpi_exit();
return ret;
}
force_fan = 0;
key_event_valid = 0;
/* enable event fifo */
hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) {
toshiba_acpi_exit();
return -ENODEV;
} else {
status = add_device();
if (ACPI_FAILURE(status)) {
toshiba_acpi_exit();
return -ENODEV;
}
}
toshiba_backlight_device = backlight_device_register("toshiba",
&toshiba_acpi.p_dev->dev,
NULL,
&toshiba_backlight_data);
if (IS_ERR(toshiba_backlight_device)) {
ret = PTR_ERR(toshiba_backlight_device);
printk(KERN_ERR "Could not register toshiba backlight device\n");
toshiba_backlight_device = NULL;
toshiba_acpi_exit();
return ret;
}
toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
/* Register rfkill switch for Bluetooth */
if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name,
&toshiba_acpi.p_dev->dev,
RFKILL_TYPE_BLUETOOTH,
&toshiba_rfk_ops,
&toshiba_acpi);
if (!toshiba_acpi.bt_rfk) {
printk(MY_ERR "unable to allocate rfkill device\n");
toshiba_acpi_exit();
return -ENOMEM;
}
ret = rfkill_register(toshiba_acpi.bt_rfk);
if (ret) {
printk(MY_ERR "unable to register rfkill device\n");
rfkill_destroy(toshiba_acpi.bt_rfk);
toshiba_acpi_exit();
return ret;
}
}
return 0;
}
module_init(toshiba_acpi_init);
module_exit(toshiba_acpi_exit);

View File

@@ -0,0 +1,737 @@
/*
* ACPI-WMI mapping driver
*
* Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
*
* GUID parsing code from ldm.c is:
* Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
* Copyright (c) 2001-2007 Anton Altaparmakov
* Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.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/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
ACPI_MODULE_NAME("wmi");
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
MODULE_LICENSE("GPL");
#define ACPI_WMI_CLASS "wmi"
#define PREFIX "ACPI: WMI: "
static DEFINE_MUTEX(wmi_data_lock);
struct guid_block {
char guid[16];
union {
char object_id[2];
struct {
unsigned char notify_id;
unsigned char reserved;
};
};
u8 instance_count;
u8 flags;
};
struct wmi_block {
struct list_head list;
struct guid_block gblock;
acpi_handle handle;
wmi_notify_handler handler;
void *handler_data;
};
static struct wmi_block wmi_blocks;
/*
* If the GUID data block is marked as expensive, we must enable and
* explicitily disable data collection.
*/
#define ACPI_WMI_EXPENSIVE 0x1
#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
static int acpi_wmi_remove(struct acpi_device *device, int type);
static int acpi_wmi_add(struct acpi_device *device);
static void acpi_wmi_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id wmi_device_ids[] = {
{"PNP0C14", 0},
{"pnp0c14", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
static struct acpi_driver acpi_wmi_driver = {
.name = "wmi",
.class = ACPI_WMI_CLASS,
.ids = wmi_device_ids,
.ops = {
.add = acpi_wmi_add,
.remove = acpi_wmi_remove,
.notify = acpi_wmi_notify,
},
};
/*
* GUID parsing functions
*/
/**
* wmi_parse_hexbyte - Convert a ASCII hex number to a byte
* @src: Pointer to at least 2 characters to convert.
*
* Convert a two character ASCII hex string to a number.
*
* Return: 0-255 Success, the byte was parsed correctly
* -1 Error, an invalid character was supplied
*/
static int wmi_parse_hexbyte(const u8 *src)
{
unsigned int x; /* For correct wrapping */
int h;
/* high part */
x = src[0];
if (x - '0' <= '9' - '0') {
h = x - '0';
} else if (x - 'a' <= 'f' - 'a') {
h = x - 'a' + 10;
} else if (x - 'A' <= 'F' - 'A') {
h = x - 'A' + 10;
} else {
return -1;
}
h <<= 4;
/* low part */
x = src[1];
if (x - '0' <= '9' - '0')
return h | (x - '0');
if (x - 'a' <= 'f' - 'a')
return h | (x - 'a' + 10);
if (x - 'A' <= 'F' - 'A')
return h | (x - 'A' + 10);
return -1;
}
/**
* wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
* @src: Memory block holding binary GUID (16 bytes)
* @dest: Memory block to hold byte swapped binary GUID (16 bytes)
*
* Byte swap a binary GUID to match it's real GUID value
*/
static void wmi_swap_bytes(u8 *src, u8 *dest)
{
int i;
for (i = 0; i <= 3; i++)
memcpy(dest + i, src + (3 - i), 1);
for (i = 0; i <= 1; i++)
memcpy(dest + 4 + i, src + (5 - i), 1);
for (i = 0; i <= 1; i++)
memcpy(dest + 6 + i, src + (7 - i), 1);
memcpy(dest + 8, src + 8, 8);
}
/**
* wmi_parse_guid - Convert GUID from ASCII to binary
* @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @dest: Memory block to hold binary GUID (16 bytes)
*
* N.B. The GUID need not be NULL terminated.
*
* Return: 'true' @dest contains binary GUID
* 'false' @dest contents are undefined
*/
static bool wmi_parse_guid(const u8 *src, u8 *dest)
{
static const int size[] = { 4, 2, 2, 2, 6 };
int i, j, v;
if (src[8] != '-' || src[13] != '-' ||
src[18] != '-' || src[23] != '-')
return false;
for (j = 0; j < 5; j++, src++) {
for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
v = wmi_parse_hexbyte(src);
if (v < 0)
return false;
}
}
return true;
}
static bool find_guid(const char *guid_string, struct wmi_block **out)
{
char tmp[16], guid_input[16];
struct wmi_block *wblock;
struct guid_block *block;
struct list_head *p;
wmi_parse_guid(guid_string, tmp);
wmi_swap_bytes(tmp, guid_input);
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
if (memcmp(block->guid, guid_input, 16) == 0) {
if (out)
*out = wblock;
return 1;
}
}
return 0;
}
static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
{
struct guid_block *block = NULL;
char method[5];
struct acpi_object_list input;
union acpi_object params[1];
acpi_status status;
acpi_handle handle;
block = &wblock->gblock;
handle = wblock->handle;
if (!block)
return AE_NOT_EXIST;
input.count = 1;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = enable;
snprintf(method, 5, "WE%02X", block->notify_id);
status = acpi_evaluate_object(handle, method, &input, NULL);
if (status != AE_OK && status != AE_NOT_FOUND)
return status;
else
return AE_OK;
}
/*
* Exported WMI functions
*/
/**
* wmi_evaluate_method - Evaluate a WMI method
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* @method_id: Method ID to call
* &in: Buffer containing input for the method call
* &out: Empty buffer to return the method results
*
* Call an ACPI-WMI method
*/
acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
acpi_handle handle;
acpi_status status;
struct acpi_object_list input;
union acpi_object params[3];
char method[5] = "WM";
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
block = &wblock->gblock;
handle = wblock->handle;
if (!(block->flags & ACPI_WMI_METHOD))
return AE_BAD_DATA;
if (block->instance_count < instance)
return AE_BAD_PARAMETER;
input.count = 2;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = instance;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = method_id;
if (in) {
input.count = 3;
if (block->flags & ACPI_WMI_STRING) {
params[2].type = ACPI_TYPE_STRING;
} else {
params[2].type = ACPI_TYPE_BUFFER;
}
params[2].buffer.length = in->length;
params[2].buffer.pointer = in->pointer;
}
strncat(method, block->object_id, 2);
status = acpi_evaluate_object(handle, method, &input, out);
return status;
}
EXPORT_SYMBOL_GPL(wmi_evaluate_method);
/**
* wmi_query_block - Return contents of a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &out: Empty buffer to return the contents of the data block to
*
* Return the contents of an ACPI-WMI data block to a buffer
*/
acpi_status wmi_query_block(const char *guid_string, u8 instance,
struct acpi_buffer *out)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
acpi_handle handle, wc_handle;
acpi_status status, wc_status = AE_ERROR;
struct acpi_object_list input, wc_input;
union acpi_object wc_params[1], wq_params[1];
char method[5];
char wc_method[5] = "WC";
if (!guid_string || !out)
return AE_BAD_PARAMETER;
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
block = &wblock->gblock;
handle = wblock->handle;
if (block->instance_count < instance)
return AE_BAD_PARAMETER;
/* Check GUID is a data block */
if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
return AE_ERROR;
input.count = 1;
input.pointer = wq_params;
wq_params[0].type = ACPI_TYPE_INTEGER;
wq_params[0].integer.value = instance;
/*
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
* enable collection.
*/
if (block->flags & ACPI_WMI_EXPENSIVE) {
wc_input.count = 1;
wc_input.pointer = wc_params;
wc_params[0].type = ACPI_TYPE_INTEGER;
wc_params[0].integer.value = 1;
strncat(wc_method, block->object_id, 2);
/*
* Some GUIDs break the specification by declaring themselves
* expensive, but have no corresponding WCxx method. So we
* should not fail if this happens.
*/
wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
if (ACPI_SUCCESS(wc_status))
wc_status = acpi_evaluate_object(handle, wc_method,
&wc_input, NULL);
}
strcpy(method, "WQ");
strncat(method, block->object_id, 2);
status = acpi_evaluate_object(handle, method, &input, out);
/*
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
* the WQxx method failed - we should disable collection anyway.
*/
if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
wc_params[0].integer.value = 0;
status = acpi_evaluate_object(handle,
wc_method, &wc_input, NULL);
}
return status;
}
EXPORT_SYMBOL_GPL(wmi_query_block);
/**
* wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* &in: Buffer containing new values for the data block
*
* Write the contents of the input buffer to an ACPI-WMI data block
*/
acpi_status wmi_set_block(const char *guid_string, u8 instance,
const struct acpi_buffer *in)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
acpi_handle handle;
struct acpi_object_list input;
union acpi_object params[2];
char method[5] = "WS";
if (!guid_string || !in)
return AE_BAD_DATA;
if (!find_guid(guid_string, &wblock))
return AE_ERROR;
block = &wblock->gblock;
handle = wblock->handle;
if (block->instance_count < instance)
return AE_BAD_PARAMETER;
/* Check GUID is a data block */
if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
return AE_ERROR;
input.count = 2;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = instance;
if (block->flags & ACPI_WMI_STRING) {
params[1].type = ACPI_TYPE_STRING;
} else {
params[1].type = ACPI_TYPE_BUFFER;
}
params[1].buffer.length = in->length;
params[1].buffer.pointer = in->pointer;
strncat(method, block->object_id, 2);
return acpi_evaluate_object(handle, method, &input, NULL);
}
EXPORT_SYMBOL_GPL(wmi_set_block);
/**
* wmi_install_notify_handler - Register handler for WMI events
* @handler: Function to handle notifications
* @data: Data to be returned to handler when event is fired
*
* Register a handler for events sent to the ACPI-WMI mapper device.
*/
acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler handler, void *data)
{
struct wmi_block *block;
acpi_status status;
if (!guid || !handler)
return AE_BAD_PARAMETER;
find_guid(guid, &block);
if (!block)
return AE_NOT_EXIST;
if (block->handler)
return AE_ALREADY_ACQUIRED;
block->handler = handler;
block->handler_data = data;
status = wmi_method_enable(block, 1);
return status;
}
EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
/**
* wmi_uninstall_notify_handler - Unregister handler for WMI events
*
* Unregister handler for events sent to the ACPI-WMI mapper device.
*/
acpi_status wmi_remove_notify_handler(const char *guid)
{
struct wmi_block *block;
acpi_status status;
if (!guid)
return AE_BAD_PARAMETER;
find_guid(guid, &block);
if (!block)
return AE_NOT_EXIST;
if (!block->handler)
return AE_NULL_ENTRY;
status = wmi_method_enable(block, 0);
block->handler = NULL;
block->handler_data = NULL;
return status;
}
EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
/**
* wmi_get_event_data - Get WMI data associated with an event
*
* @event: Event to find
* @out: Buffer to hold event data. out->pointer should be freed with kfree()
*
* Returns extra data associated with an event in WMI.
*/
acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
{
struct acpi_object_list input;
union acpi_object params[1];
struct guid_block *gblock;
struct wmi_block *wblock;
struct list_head *p;
input.count = 1;
input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event;
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock;
if ((gblock->flags & ACPI_WMI_EVENT) &&
(gblock->notify_id == event))
return acpi_evaluate_object(wblock->handle, "_WED",
&input, out);
}
return AE_NOT_FOUND;
}
EXPORT_SYMBOL_GPL(wmi_get_event_data);
/**
* wmi_has_guid - Check if a GUID is available
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
*
* Check if a given GUID is defined by _WDG
*/
bool wmi_has_guid(const char *guid_string)
{
return find_guid(guid_string, NULL);
}
EXPORT_SYMBOL_GPL(wmi_has_guid);
/*
* Parse the _WDG method for the GUID data blocks
*/
static __init acpi_status parse_wdg(acpi_handle handle)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
struct guid_block *gblock;
struct wmi_block *wblock;
acpi_status status;
u32 i, total;
status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
if (ACPI_FAILURE(status))
return status;
obj = (union acpi_object *) out.pointer;
if (obj->type != ACPI_TYPE_BUFFER)
return AE_ERROR;
total = obj->buffer.length / sizeof(struct guid_block);
gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
if (!gblock)
return AE_NO_MEMORY;
memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
for (i = 0; i < total; i++) {
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock)
return AE_NO_MEMORY;
wblock->gblock = gblock[i];
wblock->handle = handle;
list_add_tail(&wblock->list, &wmi_blocks.list);
}
kfree(out.pointer);
kfree(gblock);
return status;
}
/*
* WMI can have EmbeddedControl access regions. In which case, we just want to
* hand these off to the EC driver.
*/
static acpi_status
acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
u32 bits, acpi_integer * value,
void *handler_context, void *region_context)
{
int result = 0, i = 0;
u8 temp = 0;
if ((address > 0xFF) || !value)
return AE_BAD_PARAMETER;
if (function != ACPI_READ && function != ACPI_WRITE)
return AE_BAD_PARAMETER;
if (bits != 8)
return AE_BAD_PARAMETER;
if (function == ACPI_READ) {
result = ec_read(address, &temp);
(*value) |= ((acpi_integer)temp) << i;
} else {
temp = 0xff & ((*value) >> i);
result = ec_write(address, temp);
}
switch (result) {
case -EINVAL:
return AE_BAD_PARAMETER;
break;
case -ENODEV:
return AE_NOT_FOUND;
break;
case -ETIME:
return AE_TIME;
break;
default:
return AE_OK;
}
}
static void acpi_wmi_notify(struct acpi_device *device, u32 event)
{
struct guid_block *block;
struct wmi_block *wblock;
struct list_head *p;
list_for_each(p, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
if ((block->flags & ACPI_WMI_EVENT) &&
(block->notify_id == event)) {
if (wblock->handler)
wblock->handler(event, wblock->handler_data);
acpi_bus_generate_netlink_event(
device->pnp.device_class, dev_name(&device->dev),
event, 0);
break;
}
}
}
static int acpi_wmi_remove(struct acpi_device *device, int type)
{
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
return 0;
}
static int __init acpi_wmi_add(struct acpi_device *device)
{
acpi_status status;
int result = 0;
status = acpi_install_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler,
NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
status = parse_wdg(device->handle);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Error installing EC region handler\n");
return -ENODEV;
}
return result;
}
static int __init acpi_wmi_init(void)
{
int result;
INIT_LIST_HEAD(&wmi_blocks.list);
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&acpi_wmi_driver);
if (result < 0) {
printk(KERN_INFO PREFIX "Error loading mapper\n");
} else {
printk(KERN_INFO PREFIX "Mapper loaded\n");
}
return result;
}
static void __exit acpi_wmi_exit(void)
{
struct list_head *p, *tmp;
struct wmi_block *wblock;
acpi_bus_unregister_driver(&acpi_wmi_driver);
list_for_each_safe(p, tmp, &wmi_blocks.list) {
wblock = list_entry(p, struct wmi_block, list);
list_del(p);
kfree(wblock);
}
printk(KERN_INFO PREFIX "Mapper unloaded\n");
}
subsys_initcall(acpi_wmi_init);
module_exit(acpi_wmi_exit);