import of tp-smapi version 0.32

This commit is contained in:
Shem Multinymous 2007-03-08 00:50:50 +01:00 committed by Evgeni Golov
parent c422dec63d
commit 04dc589904
15 changed files with 215 additions and 298 deletions

14
CHANGES
View File

@ -1,5 +1,19 @@
Change history for tp_smapi:
0.32 2007-07-28
---------------------
- hdaps: Added a second input device which publishes the raw sensor position,
without joystick fuzz. In conjunction with a new hdapsd, this lets us
avoid the frequent redundant interrupts caused by hdapsd.
- hdaps: Stop polling the hardware when no one is listening. This prevents
unnecessary interrupts on tickless kernel. (Contributed by Michael Riepe.)
- In the patch generated by "make patch", thinkpad_ec and tp_smapi now reside
under drivers/misc instead of drivers/platform.
- "make patch" fixed for kernel 2.6.22.
- Removed kludge for kernels lacking the DMI OEM String decode patch.
- Now requires kernel >= 2.6.19 (due to above).
- Removed the useless "enable_pci_power_saving_on_boot" sysfs attribute.
0.31 2007-03-07
---------------------
Visible changes:

View File

@ -8,7 +8,7 @@ KBUILD := $(KBASE)/build
MOD_DIR := $(KBASE)/kernel
PWD := $(shell pwd)
IDIR := include/linux
TP_DIR := drivers/firmware
TP_DIR := drivers/misc
TP_MODULES := thinkpad_ec.o tp_smapi.o
SHELL := /bin/bash
@ -21,8 +21,8 @@ endif
DEBUG := 0
ifneq ($(shell [ -f $(KSRC)/include/linux/platform_device.h ] && echo 1),1)
$(error This driver requires kernel 2.6.15 or newer, and matching kernel headers.)
ifneq ($(shell [ -f $(KSRC)/include/linux/aio_abi.h ] && echo 1),1)
$(error This driver requires kernel 2.6.19 or newer, and matching kernel sources. You may need to set KSRC and KBUILD to find these.)
endif
.PHONY: default clean modules load unload install patch check_hdaps mk-hdaps.diff
@ -34,7 +34,7 @@ export TP_MODULES
default: modules
# Build the modules thinkpad_ec.ko, tp_smapi.ko and (if HDAPS=1) hdaps.ko
modules: $(KSRC) dmi_ec_oem_string.h $(patsubst %.o,%.c,$(TP_MODULES))
modules: $(KSRC) $(patsubst %.o,%.c,$(TP_MODULES))
$(MAKE) -C $(KSRC) M=$(PWD) O=$(KBUILD) modules
clean:
@ -44,7 +44,6 @@ clean:
rm -f *~ diff/*~ *.orig diff/*.orig *.rej diff/*.rej
rm -f tp_smapi-*-for-*.patch
rm -fr .tmp_versions Modules.symvers diff/hdaps.diff.tmp
rm -f dmi_ec_oem_string.h
load: check_hdaps unload modules
@( [ `id -u` == 0 ] || { echo "Must be root to load modules"; exit 1; } )
@ -71,6 +70,7 @@ endif
install: modules
@( [ `id -u` == 0 ] || { echo "Must be root to install modules"; exit 1; } )
rm -f $(MOD_DIR)/$(TP_DIR)/{thinkpad_ec,tp_smapi,tp_base}.ko
rm -f $(MOD_DIR)/drivers/firmware/{thinkpad_ec,tp_smapi,tp_base}.ko
rm -f $(MOD_DIR)/extra/{thinkpad_ec,tp_smapi,tp_base}.ko
ifeq ($(HDAPS),1)
rm -f $(MOD_DIR)/drivers/hwmon/hdaps.ko
@ -80,29 +80,6 @@ endif
depmod -a
# Ugly kludge for kernels that can't report OEM Strings DMI information:
ifeq ($(shell grep -q DMI_DEV_TYPE_OEM_STRING $(KSRC)/include/linux/dmi.h || echo 1),1)
ifeq ($(shell sudo /usr/sbin/dmidecode | grep -q dmidecode || echo 1),1)
$(error Could not run /usr/sbin/dmidecode. Must be root or get sudo password to do that.)
endif
GEN_DMI_DECODE_PATCH= \
cat $(PWD)/diff/dmi-decode-and-save-oem-string-information.patch | \
{ [ -f $(KSRC)/drivers/firmware/dmi_scan.c ] && cat || \
perl -pe 's@drivers/firmware/dmi_scan.c@arch/i386/kernel/dmi_scan.c@g'; }
DMI_EC_OEM_STRING:=$(shell sudo /usr/sbin/dmidecode | grep 'IBM ThinkPad Embedded Controller')
dmi_ec_oem_string.h: $(KSRC)/include/linux/dmi.h
@echo 'WARNING: Your kernel does not have this patch applied:' >&2
@echo ' dmi-decode-and-save-oem-string-information.patch' >&2
@echo ' So I am hard-coding machine-specific DMI information into your driver.' >&2
echo '/* This is machine-specific DMI information. */' > $@
echo '#define DMI_EC_OEM_STRING_KLUDGE "$(DMI_EC_OEM_STRING)"' >> $@
else
NO_DMI_KLUDGE_DIFF=$(PWD)/diff/thinkpad_ec-no-dmi-kludge.diff
GEN_DMI_DECODE_PATCH=cat /dev/null
dmi_ec_oem_string.h: $(KSRC)/include/linux/dmi.h
echo '/* Intentionally empty. You have proper DMI OEM Strings. */' > $@
endif
#####################################################################
# Generate a stand-alone kernel patch
@ -116,8 +93,8 @@ SMAPI_IN_PATCH := 1
HDAPS_IN_PATCH := 1
patch:
TMPDIR=`mktemp -d /tmp/tp_smapi-patch.XXXXXX` &&\
echo "Work directory: $$TMPDIR" &&\
@TMPDIR=`mktemp -d /tmp/tp_smapi-patch.XXXXXX` &&\
echo "Working directory: $$TMPDIR" &&\
cd $$TMPDIR &&\
mkdir -p $(ORG)/$(TP_DIR) &&\
mkdir -p $(ORG)/$(IDIR) &&\
@ -127,32 +104,32 @@ patch:
cp -r $(ORG) $(NEW) &&\
\
if [ "$(BASE_IN_PATCH)" == 1 ]; then \
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-thinkpad_ec.diff -p1 &&\
cp $(PWD)/thinkpad_ec.c $(NEW)/$(TP_DIR)/thinkpad_ec.c &&\
cp $(PWD)/thinkpad_ec.h $(NEW)/$(IDIR)/thinkpad_ec.h &&\
patch --no-backup-if-mismatch -s -d $(NEW)/$(TP_DIR) -i $(PWD)/diff/thinkpad_ec-no-dmi-kludge.diff -p1 &&\
sed -i -e '$$aobj-$$(CONFIG_THINKPAD_EC) += thinkpad_ec.o' $(NEW)/$(TP_DIR)/Makefile \
cp $(PWD)/thinkpad_ec.c $(NEW)/$(TP_DIR)/thinkpad_ec.c &&\
cp $(PWD)/thinkpad_ec.h $(NEW)/$(IDIR)/thinkpad_ec.h &&\
perl -i -pe 'print `cat $(PWD)/diff/Kconfig-thinkpad_ec.add` if m/^endmenu$$/' $(NEW)/$(TP_DIR)/Kconfig &&\
sed -i -e '$$aobj-$$(CONFIG_THINKPAD_EC) += thinkpad_ec.o' $(NEW)/$(TP_DIR)/Makefile \
; fi &&\
\
if [ "$(HDAPS_IN_PATCH)" == 1 ]; then \
cp $(PWD)/hdaps.c $(NEW)/drivers/hwmon/ &&\
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-hdaps-select-thinkpad_ec -p1 \
cp $(PWD)/hdaps.c $(NEW)/drivers/hwmon/ &&\
perl -i -0777 -pe 's/(config SENSORS_HDAPS\n\ttristate [^\n]+\n\tdepends [^\n]+\n)/$$1\tselect THINKPAD_EC\n/' $(NEW)/drivers/hwmon/Kconfig \
; fi &&\
\
if [ "$(SMAPI_IN_PATCH)" == 1 ]; then \
sed -i -e '$$aobj-$$(CONFIG_TP_SMAPI) += tp_smapi.o' $(NEW)/$(TP_DIR)/Makefile &&\
cp $(PWD)/tp_smapi.c $(NEW)/$(TP_DIR)/tp_smapi.c &&\
patch --no-backup-if-mismatch -s -d $(NEW) -i $(PWD)/diff/Kconfig-tp_smapi.diff -p1 &&\
mkdir -p $(NEW)/Documentation &&\
perl -0777 -pe 's/\n(Installation\n---+|Conflict with HDAPS\n---+|Files in this package\n---+|Setting and getting CD-ROM speed:\n).*?\n(?=[^\n]*\n-----)/\n/gs' $(PWD)/README > $(NEW)/Documentation/tp_smapi.txt \
sed -i -e '$$aobj-$$(CONFIG_TP_SMAPI) += tp_smapi.o' $(NEW)/$(TP_DIR)/Makefile &&\
perl -i -pe 'print `cat $(PWD)/diff/Kconfig-tp_smapi.add` if m/^endmenu$$/' $(NEW)/$(TP_DIR)/Kconfig &&\
cp $(PWD)/tp_smapi.c $(NEW)/$(TP_DIR)/tp_smapi.c &&\
mkdir -p $(NEW)/Documentation &&\
perl -0777 -pe 's/\n(Installation\n---+|Conflict with HDAPS\n---+|Files in this package\n---+|Setting and getting CD-ROM speed:\n).*?\n(?=[^\n]*\n-----)/\n/gs' $(PWD)/README > $(NEW)/Documentation/tp_smapi.txt \
; fi &&\
\
{ diff -dNurp $(ORG) $(NEW) > patch \
|| [ $$? -lt 2 ]; } &&\
$(GEN_DMI_DECODE_PATCH) >> patch &&\
{ echo "Generated for $(KVER) in $(KSRC)"; echo; diffstat patch; echo; echo; cat patch; } \
> $(PWD)/${PATCH} &&\
rm -r $$TMPDIR
@echo
@diffstat ${PATCH}
@echo -e "\nPatch file created:\n ${PATCH}"
@echo -e "To apply, use:\n patch -p1 -d ${KSRC} < ${PATCH}"

44
README
View File

@ -1,4 +1,4 @@
tp_smapi version 0.31
tp_smapi version 0.32
IBM ThinkPad hardware functions driver
Author: Shem Multinymous <multinymous@gmail.com>
@ -208,6 +208,26 @@ Some of the battery status is also visible through ACPI (/proc/acpi/battery/).
The charging control files (*_charge_thresh, inhibit_charge_minutes and
force_discrage*) don't have this problem.
Beside resolving the conflict with thinkpad_ec and tp_smapi, the modified hdaps
has several improvements:
- It fixes reliability and improves support for recent ThinkPad models (*60 and
newer), since unlike the mainline driver, it correctly follows the Embedded
Controller communication protocol.
- It provides the ability to control polling rate (and several other parameters).
- It provides a second input device, which publishes the raw accelerometer
measurements (without the fuzzing needed for joystick emulation). This input
device can be matched by a udev rule such as the following:
KERNEL=="event[0-9]*", ATTRS{phys}=="hdaps/input1",
ATTRS{modalias}=="input:b0019v1014p5054e4801-*",
SYMLINK+="input/hdaps/accelerometer-event
Licensing note: all my changes to the HDAPS driver are licensed under the
GPL version 2 or, at your option and to the extent allowed by derivation from
prior works, any later version. My version of hdaps is derived work from the
mainline version, which at the time of writing is available only under
GPL version 2.
Bug reporting
-------------
@ -227,6 +247,8 @@ README
This file.
CHANGES
Change log.
TODO
Pending improvements.
Makefile
Makefile (see "Installation" above).
tp_smapi.c
@ -237,32 +259,12 @@ thinkpad_ec.*
hdaps.c
Modified version of hdaps.c driver from mainline kernel, patched to use
thinkpad_ec and several other improvements.
diff/dmi-decode-and-save-oem-string-information
A patch from the kernel -mm series, needed for hardware detection.
If you don't have it, "make load" and "make install" will use a workaround
kludge, and the result of "make patch" will include this patch.
diff/* (excluding above)
Used by "make patch" to create a clean stand-alone patch.
include/
Contains a symlink to make gcc happy in kernel builds.
Ideas for improvement
---------------------
(The best way to get these done is to send a patch.)
Don't create /sys files for unsupported functions, and don't access those
functions on suspend+resume (requires probing on module load or a huge
white/blacklist).
Make inhibit_charge_minutes return the time left, not the time originally
set (as returned by the SMAPI BIOS). Requires remembering when
inhibit_charge_minutes was set and comparing to current time.
Save and and restore inhibit_charge_minutes across suspend-to-disk, as done
for charge thresholds (requires the above time calculations too).
More about SMAPI
----------------

18
TODO Normal file
View File

@ -0,0 +1,18 @@
Ideas for improvement
---------------------
(The best way to get these done is to send a patch.)
Don't create /sys files for unsupported functions, and don't access those
functions on suspend+resume (requires probing on module load or a huge
white/blacklist).
Make inhibit_charge_minutes return the time left, not the time originally
set (as returned by the SMAPI BIOS). Requires remembering when
inhibit_charge_minutes was set and comparing to current time.
Save and and restore inhibit_charge_minutes across suspend-to-disk, as done
for charge thresholds (requires the above time calculations too).
Use the new Linux battery model considered (not yet merged into mainline).
Stop hdaps sampling when neither input devices nor sysfs are being accessed.

View File

@ -1,10 +0,0 @@
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -494,6 +494,7 @@
config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on HWMON && INPUT && X86
+ select THINKPAD_EC
default n
help
This driver provides support for the IBM Hard Drive Active Protection

View File

@ -0,0 +1,8 @@
config THINKPAD_EC
tristate
depends on X86
---help---
This is a low-level driver for accessing the ThinkPad H8S embedded
controller over the LPC bus (not to be confused with the ACPI Embedded
Controller interface).

View File

@ -1,10 +0,0 @@
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -85,4 +85,7 @@
Say Y or M here to enable the driver for use by Dell systems
management software such as Dell OpenManage.
+config THINKPAD_EC
+ tristate
+
endmenu

12
diff/Kconfig-tp_smapi.add Normal file
View File

@ -0,0 +1,12 @@
config TP_SMAPI
tristate "ThinkPad SMAPI Support"
depends on X86
select THINKPAD_EC
default n
help
This adds SMAPI support on IBM ThinkPads, mostly used for battery
charge control. For more information about this driver see
<http://www.thinkwiki.org/wiki/tp_smapi>.
If you have an IBM ThinkPad laptop, say Y or M here.

View File

@ -1,19 +0,0 @@
--- linux-2.6.15-rc5-orig/drivers/firmware/Kconfig 2005-12-13 19:13:57.000000000 +0200
+++ linux-2.6.15-rc5-patched/drivers/firmware/Kconfig 2005-12-21 00:35:13.000000000 +0200
@@ -88,4 +88,16 @@
config THINKPAD_EC
tristate
+config TP_SMAPI
+ tristate "ThinkPad SMAPI Support"
+ depends on X86
+ select THINKPAD_EC
+ default n
+ help
+ This adds SMAPI support on IBM ThinkPads, mostly used for battery
+ charge control. For more information about this driver see
+ <http://www.thinkwiki.org/wiki/tp_smapi>.
+
+ If you have an IBM ThinkPad laptop, say Y or M here.
+
endmenu

View File

@ -1,77 +0,0 @@
Subject: DMI: Decode and save OEM String information
From: "Shem Multinymous" <multinymous@gmail.com>
This teaches dmi_decode() how to decode and save OEM Strings (type 11) DMI
information, which is currently discarded silently. Existing code using
DMI is not affected. Follows the "System Management BIOS (SMBIOS)
Specification" (http://www.dmtf.org/standards/smbios), and also the
userspace dmidecode.c code.
OEM Strings are the only safe way to identify some hardware, e.g., the
ThinkPad embedded controller used by the soon-to-be-submitted tp_smapi
driver. This will also let us eliminate the long whitelist in the mainline
hdaps driver (in a future patch).
Signed-off-by: Shem Multinymous <multinymous@gmail.com>
Cc: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---
drivers/firmware/dmi_scan.c | 23 +++++++++++++++++++++++
include/linux/dmi.h | 3 ++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff -puN drivers/firmware/dmi_scan.c~dmi-decode-and-save-oem-string-information drivers/firmware/dmi_scan.c
--- a/drivers/firmware/dmi_scan.c~dmi-decode-and-save-oem-string-information
+++ a/drivers/firmware/dmi_scan.c
@@ -123,6 +123,26 @@ static void __init dmi_save_devices(stru
dev->type = *d++ & 0x7f;
dev->name = dmi_string(dm, *d);
dev->device_data = NULL;
+ list_add(&dev->list, &dmi_devices);
+ }
+}
+
+static void __init dmi_save_oem_strings_devices(struct dmi_header *dm)
+{
+ int i, count = *(u8 *)(dm + 1);
+ struct dmi_device *dev;
+
+ for (i = 1; i <= count; i++) {
+ dev = dmi_alloc(sizeof(*dev));
+ if (!dev) {
+ printk(KERN_ERR
+ "dmi_save_oem_strings_devices: out of memory.\n");
+ break;
+ }
+
+ dev->type = DMI_DEV_TYPE_OEM_STRING;
+ dev->name = dmi_string(dm, i);
+ dev->device_data = NULL;
list_add(&dev->list, &dmi_devices);
}
@@ -181,6 +201,9 @@ static void __init dmi_decode(struct dmi
case 10: /* Onboard Devices Information */
dmi_save_devices(dm);
break;
+ case 11: /* OEM Strings */
+ dmi_save_oem_strings_devices(dm);
+ break;
case 38: /* IPMI Device Information */
dmi_save_ipmi_device(dm);
}
diff -puN include/linux/dmi.h~dmi-decode-and-save-oem-string-information include/linux/dmi.h
--- a/include/linux/dmi.h~dmi-decode-and-save-oem-string-information
+++ a/include/linux/dmi.h
@@ -27,7 +27,8 @@ enum dmi_device_type {
DMI_DEV_TYPE_ETHERNET,
DMI_DEV_TYPE_TOKENRING,
DMI_DEV_TYPE_SOUND,
- DMI_DEV_TYPE_IPMI = -1
+ DMI_DEV_TYPE_IPMI = -1,
+ DMI_DEV_TYPE_OEM_STRING = -2
};
struct dmi_header {
_

View File

@ -1,12 +0,0 @@
--- orig/thinkpad_ec.c 2006-08-02 00:00:00.000000000 +0000
+++ no-dmi-kludge/thinkpad_ec.c 2006-08-02 00:00:00.000000000 +0000
@@ -402,9 +402,4 @@ static int __init check_for_embedded_con
};
-#include "dmi_ec_oem_string.h"
-#ifdef DMI_EC_OEM_STRING_KLUDGE
- return (strstr(DMI_EC_OEM_STRING_KLUDGE, "IBM ThinkPad Embedded Controller")) ||
-#else
return dmi_find_substring(DMI_DEV_TYPE_OEM_STRING,
"IBM ThinkPad Embedded Controller") ||
-#endif
dmi_check_system(tp_whitelist);

106
hdaps.c
View File

@ -32,9 +32,10 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/thinkpad_ec.h>
#include <linux/pci_ids.h>
/* Embedded controller accelerometer read command and its result: */
static const struct thinkpad_ec_row ec_accel_args =
@ -62,9 +63,16 @@ static const struct thinkpad_ec_row ec_accel_args =
#define HDAPS_INPUT_FLAT 4
#define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistance */
/* Input IDs */
#define HDAPS_INPUT_VENDOR PCI_VENDOR_ID_IBM
#define HDAPS_INPUT_PRODUCT 0x5054 /* "TP", shared with thinkpad_acpi */
#define HDAPS_INPUT_JS_VERSION 0x6801 /* Joystick emulation input device */
#define HDAPS_INPUT_RAW_VERSION 0x4801 /* Raw accelerometer input device */
static struct timer_list hdaps_timer;
static struct platform_device *pdev;
static struct input_dev *hdaps_idev;
static struct input_dev *hdaps_idev; /* joystick-like device with fuzz */
static struct input_dev *hdaps_idev_raw; /* raw hdaps sensor readouts */
static unsigned int hdaps_invert;
static int needs_calibration;
@ -84,6 +92,11 @@ static int rest_x, rest_y; /* calibrated rest position */
/* Last time we saw keyboard and mouse activity: */
static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
static u64 last_mouse_jiffies = INITIAL_JIFFIES;
static u64 last_update_jiffies = INITIAL_JIFFIES;
/* input device use count */
static int hdaps_users;
static DEFINE_MUTEX(hdaps_users_mtx);
/* Some models require an axis transformation to the standard reprsentation */
static void transform_axes(int *x, int *y)
@ -144,6 +157,7 @@ static int __hdaps_update(int fast)
temperature = data.val[EC_ACCEL_IDX_TEMP1];
last_update_jiffies = get_jiffies_64();
stale_readout = 0;
if (needs_calibration) {
rest_x = pos_x;
@ -164,9 +178,11 @@ static int __hdaps_update(int fast)
*/
static int hdaps_update(void)
{
u64 age = get_jiffies_64() - last_update_jiffies;
int total, ret;
if (!stale_readout) /* already updated recently? */
return 0;
if (!stale_readout && age < (9*HZ)/(10*sampling_rate))
return 0; /* already updated recently */
for (total=0; total<READ_TIMEOUT_MSECS; total+=RETRY_MSECS) {
ret = thinkpad_ec_lock();
if (ret)
@ -419,7 +435,11 @@ static int hdaps_resume(struct platform_device *dev)
int ret = hdaps_device_init();
if (ret)
return ret;
mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
mutex_lock(&hdaps_users_mtx);
if (hdaps_users)
mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
mutex_unlock(&hdaps_users_mtx);
return 0;
}
@ -471,6 +491,9 @@ keep_active:
input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
input_sync(hdaps_idev);
input_report_abs(hdaps_idev_raw, ABS_X, pos_x);
input_report_abs(hdaps_idev_raw, ABS_Y, pos_y);
input_sync(hdaps_idev_raw);
mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
}
@ -563,7 +586,7 @@ static ssize_t hdaps_sampling_rate_store(
const char *buf, size_t count)
{
int rate, ret;
if (sscanf(buf, "%d", &rate) != 1 || rate>HZ || rate<0) {
if (sscanf(buf, "%d", &rate) != 1 || rate>HZ || rate<=0) {
printk(KERN_WARNING
"must have 0<input_sampling_rate<=HZ=%d\n", HZ);
return -EINVAL;
@ -645,6 +668,28 @@ static ssize_t hdaps_fake_data_mode_show(
return sprintf(buf, "%d\n", fake_data_mode);
}
static int hdaps_mousedev_open(struct input_dev *dev)
{
if (!try_module_get(THIS_MODULE))
return -ENODEV;
mutex_lock(&hdaps_users_mtx);
if (hdaps_users++ == 0) /* first input user */
mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
mutex_unlock(&hdaps_users_mtx);
return 0;
}
static void hdaps_mousedev_close(struct input_dev *dev)
{
mutex_lock(&hdaps_users_mtx);
if (--hdaps_users == 0) /* no input users left */
del_timer_sync(&hdaps_timer);
mutex_unlock(&hdaps_users_mtx);
module_put(THIS_MODULE);
}
static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
/* "temp1" instead of "temperature" is hwmon convention */
@ -685,7 +730,7 @@ static struct attribute_group hdaps_attribute_group = {
/* Module stuff */
/* hdaps_dmi_match_invert - found an inverted match. */
static int hdaps_dmi_match_invert(struct dmi_system_id *id)
static int __init hdaps_dmi_match_invert(struct dmi_system_id *id)
{
hdaps_invert = 1;
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
@ -693,7 +738,7 @@ static int hdaps_dmi_match_invert(struct dmi_system_id *id)
return 1;
}
#define HDAPS_DMI_MATCH_INVERT(vendor,model) { \
#define HDAPS_DMI_MATCH_INVERT(vendor, model) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match_invert, \
.matches = { \
@ -706,9 +751,7 @@ static int __init hdaps_init(void)
{
int ret;
/* List of models with abnormal axis configuration.
Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
"ThinkPad T42p", so the order of the entries matters */
/* List of models with abnormal axis configuration. */
struct dmi_system_id hdaps_whitelist[] = {
HDAPS_DMI_MATCH_INVERT("IBM","ThinkPad R50p"),
HDAPS_DMI_MATCH_INVERT("IBM","ThinkPad T41p"),
@ -743,13 +786,26 @@ static int __init hdaps_init(void)
goto out_group;
}
hdaps_idev_raw = input_allocate_device();
if (!hdaps_idev_raw) {
ret = -ENOMEM;
goto out_idev_first;
}
/* calibration for the input device (deferred to avoid delay) */
needs_calibration = 1;
/* initialize the input class */
hdaps_idev->name = "hdaps";
/* initialize the joystick-like fuzzed input device */
hdaps_idev->name = "ThinkPad HDAPS joystick emulation";
hdaps_idev->phys = "hdaps/input0";
hdaps_idev->id.bustype = BUS_HOST;
hdaps_idev->id.vendor = HDAPS_INPUT_VENDOR;
hdaps_idev->id.product = HDAPS_INPUT_PRODUCT;
hdaps_idev->id.version = HDAPS_INPUT_JS_VERSION;
hdaps_idev->cdev.dev = &pdev->dev;
hdaps_idev->evbit[0] = BIT(EV_ABS);
hdaps_idev->open = hdaps_mousedev_open;
hdaps_idev->close = hdaps_mousedev_close;
input_set_abs_params(hdaps_idev, ABS_X,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
input_set_abs_params(hdaps_idev, ABS_Y,
@ -759,12 +815,32 @@ static int __init hdaps_init(void)
if (ret)
goto out_idev;
mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
/* initialize the raw data input device */
hdaps_idev_raw->name = "ThinkPad HDAPS accelerometer data";
hdaps_idev_raw->phys = "hdaps/input1";
hdaps_idev_raw->id.bustype = BUS_HOST;
hdaps_idev_raw->id.vendor = HDAPS_INPUT_VENDOR;
hdaps_idev_raw->id.product = HDAPS_INPUT_PRODUCT;
hdaps_idev_raw->id.version = HDAPS_INPUT_RAW_VERSION;
hdaps_idev_raw->cdev.dev = &pdev->dev;
hdaps_idev_raw->evbit[0] = BIT(EV_ABS);
hdaps_idev_raw->open = hdaps_mousedev_open;
hdaps_idev_raw->close = hdaps_mousedev_close;
input_set_abs_params(hdaps_idev_raw, ABS_X, -32768, 32767, 0, 0);
input_set_abs_params(hdaps_idev_raw, ABS_Y, -32768, 32767, 0, 0);
ret = input_register_device(hdaps_idev_raw);
if (ret)
goto out_idev_reg_first;
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
return 0;
out_idev_reg_first:
input_unregister_device(hdaps_idev);
out_idev:
input_free_device(hdaps_idev_raw);
out_idev_first:
input_free_device(hdaps_idev);
out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
@ -780,7 +856,7 @@ out:
static void __exit hdaps_exit(void)
{
del_timer_sync(&hdaps_timer);
input_unregister_device(hdaps_idev_raw);
input_unregister_device(hdaps_idev);
hdaps_device_shutdown(); /* ignore errors, effect is negligible */
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);

View File

@ -6,11 +6,12 @@
* The interface provides various system management services (currently
* known: battery information and accelerometer readouts). This driver
* provides access and mutual exclusion for the EC interface.
* For information about the LPC protocol and terminology, see:
*
* The LPC protocol and terminology is documented here:
* "H8S/2104B Group Hardware Manual",
* http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
*
* Copyright (C) 2006 Shem Multinymous <multinymous@gmail.com>
* Copyright (C) 2006-2007 Shem Multinymous <multinymous@gmail.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
@ -37,7 +38,7 @@
#include <asm/semaphore.h>
#include <asm/io.h>
#define TP_VERSION "0.31"
#define TP_VERSION "0.32"
MODULE_AUTHOR("Shem Multinymous");
MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
@ -125,7 +126,14 @@ void thinkpad_ec_unlock(void)
EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
/* Tell embedded controller to prepare a row */
/**
* thinkpad_ec_request_row - tell embedded controller to prepare a row
* @args Input register arguments
*
* Requests a data row by writing to H8S LPC registers TRW0 through TWR15 (or
* a subset thereof) following the protocol prescribed by the "H8S/2104B Group
* Hardware Manual". Does sanity checks via status register STR3.
*/
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
{
u8 str3;
@ -193,8 +201,13 @@ static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
return -EIO;
}
/* Read current row data from the controller, assuming it's already
* requested.
/**
* thinkpad_ec_read_data - read pre-requested row-data from EC
* @args Input register arguments of pre-requested rows
* @data Output register values
*
* Reads current row data from the controller, assuming it's already
* requested. Follows the H8S spec for register access and status checks.
*/
static int thinkpad_ec_read_data(const struct thinkpad_ec_row *args,
struct thinkpad_ec_row *data)
@ -383,9 +396,13 @@ EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
/*** Checking for EC hardware ***/
/* thinkpad_ec_test:
/**
* thinkpad_ec_test - verify the EC is present and follows protocol
*
* Ensure the EC LPC3 channel really works on this machine by making
* an arbitrary harmless EC request and seeing if the EC follows protocol.
* an EC request and seeing if the EC follows the documented H8S protocol.
* The requested row just reads battery status, so it should be harmless to
* access it (on a correct EC).
* This test writes to IO ports, so execute only after checking DMI.
*/
static int __init thinkpad_ec_test(void)
@ -431,13 +448,8 @@ static int __init check_dmi_for_ec(void)
TP_DMI_MATCH("IBM","ThinkPad X24"),
{ .ident = NULL }
};
#include "dmi_ec_oem_string.h"
#ifdef DMI_EC_OEM_STRING_KLUDGE
return (strstr(DMI_EC_OEM_STRING_KLUDGE, "IBM ThinkPad Embedded Controller")) ||
#else
return dmi_find_substring(DMI_DEV_TYPE_OEM_STRING,
"IBM ThinkPad Embedded Controller") ||
#endif
dmi_check_system(tp_whitelist);
}

View File

@ -1,6 +1,6 @@
/*
* thinkpad_ec.h - interface to ThinkPad embedded controller LPC3 functions
*
*
* Copyright (C) 2005 Shem Multinymous <multinymous@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@ -31,8 +31,8 @@ struct thinkpad_ec_row {
u8 val[TP_CONTROLLER_ROW_LEN];
};
extern int thinkpad_ec_lock(void);
extern int thinkpad_ec_try_lock(void);
extern int __must_check thinkpad_ec_lock(void);
extern int __must_check thinkpad_ec_try_lock(void);
extern void thinkpad_ec_unlock(void);
extern int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,

View File

@ -41,7 +41,7 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#define TP_VERSION "0.31"
#define TP_VERSION "0.32"
#define TP_DESC "ThinkPad SMAPI Support"
#define TP_DIR "smapi"
@ -75,8 +75,6 @@ MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)");
#define SMAPI_SET_FORCE_DISCHARGE 0x2119
#define SMAPI_GET_THRESH_STOP 0x211a
#define SMAPI_SET_THRESH_STOP 0x211b
#define SMAPI_GET_PCI_BUS_POWER_SAVING 0x4004
#define SMAPI_SET_PCI_BUS_POWER_SAVING 0x4005
/* SMAPI error codes (see ThinkPad 770 Technical Reference Manual p.83 at
http://www-307.ibm.com/pc/support/site.wss/document.do?lndocid=PFAN-3TUQQD */
@ -439,51 +437,6 @@ static int set_force_discharge(int bat, int enabled)
return ret;
}
/**
* get_enable_pci_power_saving_on_boot - get PCI power enable status via SMAPI
* @on: 1 iff PCI bus power saving will be enabled in the next reboot
*/
static int get_enable_pci_power_saving_on_boot(int *on)
{
u32 ebx, esi;
const char* msg;
int ret = smapi_request(SMAPI_GET_PCI_BUS_POWER_SAVING, 0,0,0,
&ebx, NULL, NULL, NULL, &esi, &msg);
if (ret) {
TPRINTK(KERN_NOTICE, "failed: %s",msg);
return ret;
}
if (!(ebx & 0x00000001)) {
TPRINTK(KERN_NOTICE, "unknown status ebx==0x%x esi==0x%x",
ebx, esi);
return -EIO;
}
*on = esi & 0x00000001;
return 0;
}
/**
* set_enable_pci_power_saving_on_boot - set PCI power enable status via SMAPI
* @on: 1 iff PCI bus power saving should be enabled in the next reboot
*/
static int set_enable_pci_power_saving_on_boot(int on)
{
u32 ecx, edi, esi;
const char* msg;
int ret = smapi_request(SMAPI_GET_PCI_BUS_POWER_SAVING, 0,0,0,
NULL, &ecx, NULL, &edi, &esi, &msg);
if (ret) {
TPRINTK(KERN_NOTICE, "get failed: %s", msg);
return ret;
}
esi = (esi & 0xFFFE) | (on ? 0x0001 : 0x0000);
ret = smapi_write(SMAPI_SET_PCI_BUS_POWER_SAVING,
ecx, edi, esi, &msg);
if (ret)
TPRINTK(KERN_NOTICE, "set failed: %s", msg);
return ret;
}
/*********************************************************************
* Wrappers to threshold-related SMAPI functions, which handle default
@ -1142,29 +1095,6 @@ static ssize_t show_ac_connected(
return sprintf(buf, "%d\n", ret); /* type: boolean */
}
static ssize_t show_enable_pci_power_saving_on_boot(
struct device *dev, struct device_attribute *attr, char *buf)
{
int on;
int ret = get_enable_pci_power_saving_on_boot(&on);
if (ret)
return ret;
return sprintf(buf, "%d\n", on); /* type: boolean */
}
static ssize_t store_enable_pci_power_saving_on_boot(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret;
int on;
if (sscanf(buf, "%d", &on)!=1 || on<0 || on>1)
return -EINVAL;
ret = set_enable_pci_power_saving_on_boot(on);
if (ret)
return ret;
return count;
}
/*********************************************************************
* The the "smapi_request" sysfs attribute executes a raw SMAPI call.
* You write to make a request and read to get the result. The state
@ -1268,15 +1198,11 @@ static struct platform_driver tp_driver = {
/* Attributes in /sys/devices/platform/smapi/ */
static DEVICE_ATTR(ac_connected, 0444, show_ac_connected, NULL);
static DEVICE_ATTR(enable_pci_power_saving_on_boot, 0644,
show_enable_pci_power_saving_on_boot,
store_enable_pci_power_saving_on_boot);
static DEVICE_ATTR(smapi_request, 0600, show_smapi_request,
store_smapi_request);
static struct attribute *tp_root_attributes[] = {
&dev_attr_ac_connected.attr,
&dev_attr_enable_pci_power_saving_on_boot.attr,
&dev_attr_smapi_request.attr,
NULL
};