mirror of
https://github.com/linux-thinkpad/tp_smapi.git
synced 2024-09-11 07:05:28 +03:00
import of tp-smapi version 0.28
This commit is contained in:
parent
ee4b50c735
commit
0fb18e4301
15
CHANGES
15
CHANGES
@ -1,5 +1,20 @@
|
||||
Change history for tp_smapi:
|
||||
|
||||
0.28 2006-08-16
|
||||
---------------------
|
||||
Visible changes:
|
||||
- thinkpad_ec: Removed 'debug' module parameter, it no longer affected much.
|
||||
- tp_smapi: Remove optical drive speed control (formerly #ifdefed out).
|
||||
|
||||
Internal changes:
|
||||
- hdaps: Removed __init from hdaps_check_ec(). This fixes crashes on resume
|
||||
(Thanks, Igor!).
|
||||
- hdaps: Moved axis transformation to dedicated function transform_axes().
|
||||
- tp_smapi: Cleaned up printk() output, changed macros.
|
||||
- tp_smapi: Added "E" to SMAPI parameter names (BX->EBX) - they're 32-bit.
|
||||
- tp_Smapi: Removed verbose printks()s on bad sysfs args.
|
||||
- A lot of small cleanups.
|
||||
|
||||
0.27 2006-08-05
|
||||
---------------------
|
||||
Visible changes:
|
||||
|
3
Makefile
3
Makefile
@ -46,7 +46,7 @@ clean:
|
||||
|
||||
load: check_hdaps unload modules
|
||||
@( [ `id -u` == 0 ] || { echo "Must be root to load modules"; exit 1; } )
|
||||
{ insmod ./thinkpad_ec.ko debug=$(DEBUG) &&\
|
||||
{ insmod ./thinkpad_ec.ko &&\
|
||||
insmod ./tp_smapi.ko debug=$(DEBUG) &&\
|
||||
$(LOAD_HDAPS); }; :
|
||||
@echo -e '\nRecent dmesg output:' ; dmesg | tail -10
|
||||
@ -140,7 +140,6 @@ patch:
|
||||
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)/$(TP_DIR) -i $(PWD)/diff/tp_smapi-no_cd.diff -p1 &&\
|
||||
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 \
|
||||
|
30
README
30
README
@ -1,4 +1,4 @@
|
||||
tp_smapi version 0.27
|
||||
tp_smapi version 0.28
|
||||
IBM ThinkPad hardware functions driver
|
||||
|
||||
Author: Shem Multinymous <multinymous@gmail.com>
|
||||
@ -51,25 +51,23 @@ To prepare a stand-alone patch against another kernel, follow this example:
|
||||
To delete all autogenerated files:
|
||||
# make clean
|
||||
|
||||
Append "DEBUG=1" to "make load" to load thinkpad_ec and tp_smapi with debug
|
||||
dmesg output enabled.
|
||||
Append "DEBUG=1" to "make load" to load tp_smapi with debug=1.
|
||||
|
||||
The original kernel tree is never modified by any these commands.
|
||||
The /lib/modules directory is modified only by "make install".
|
||||
|
||||
If your kernel doesn't have dmi-decode-and-save-oem-string-information
|
||||
applied (see below) then all make commands except "make patch" will need
|
||||
to invoke "dmidecode" using "sudo", to get information about your
|
||||
hardware at compile time.
|
||||
|
||||
|
||||
Module parameters
|
||||
-----------------
|
||||
|
||||
thinkpad_ec module:
|
||||
debug=1 enables verbose dmesg output.
|
||||
|
||||
tp_smapi module:
|
||||
debug=1 enables verbose dmesg output.
|
||||
|
||||
hdaps module (modified version of driver from mainline kernel):
|
||||
force=1 forces loading on ThinkPad models that are not whitelisted.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
@ -155,20 +153,6 @@ This performs raw SMAPI calls. It uses a bad interface that cannot handle
|
||||
multiple simultaneous access. Don't touch it, it's for development only.
|
||||
|
||||
|
||||
Setting and getting CD-ROM speed:
|
||||
|
||||
# echo 0 > /sys/devices/platform/smapi/cd_speed # slow
|
||||
# echo 1 > /sys/devices/platform/smapi/cd_speed # medium
|
||||
# echo 2 > /sys/devices/platform/smapi/cd_speed # fast
|
||||
# cat /sys/devices/platform/smapi/cd_speed
|
||||
|
||||
NOTE: cd_speed is disabled by default ("#define PROVIDE_CD_SPEED" to enable),
|
||||
since changing the CD speed when the CD kernel is also accessing the CD
|
||||
(e.g., reading a file) will hang your computer. You can get the same
|
||||
functionality in a safer using combination of "hdparm -E" or "eject -x" for
|
||||
CD and speedcontrol (http://safari.iki.fi/speedcontrol.c) for DVD.
|
||||
|
||||
|
||||
Model-specific status
|
||||
---------------------
|
||||
|
||||
|
@ -1,98 +0,0 @@
|
||||
--- orig/tp_smapi.c 2006-01-07 20:39:28.000000000 +0000
|
||||
+++ no_cd_speed/tp_smapi.c 2006-01-07 21:05:56.000000000 +0000
|
||||
@@ -66,2 +65,0 @@
|
||||
-/* #define PROVIDE_CD_SPEED */ /* evil - see README */
|
||||
-
|
||||
@@ -560,48 +557,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
-
|
||||
-static int get_cd_speed(int *speed) {
|
||||
- const char* msg;
|
||||
- u32 bx, dx, di;
|
||||
- int ret = smapi_request(SMAPI_GET_CDROM_STATUS, 0, 0, 0,
|
||||
- &bx, NULL, &dx, &di, NULL, &msg);
|
||||
- if (ret) {
|
||||
- printk(TP_NOTICE "cannot get cd speed: %s\n", msg);
|
||||
- return ret;
|
||||
- }
|
||||
- if (dx==0x78 && di==0x1e) {
|
||||
- *speed = 2;
|
||||
- } else if (dx==0x0f && di==0x00) {
|
||||
- *speed = 0;
|
||||
- } else {
|
||||
- *speed = 1;
|
||||
- }
|
||||
- /* what does bx&80 mean? */
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int set_cd_speed(int speed) {
|
||||
- const char* msg;
|
||||
- int ret, dummy;
|
||||
- short int cx, di;
|
||||
- ret = get_cd_speed(&dummy); /* verify read before writing */
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- if (speed==0) {
|
||||
- cx=0x0f; di=0x00;
|
||||
- } else if (speed==1) {
|
||||
- cx=0x1e; di=0x04;
|
||||
- } else {
|
||||
- cx=0x78; di=0x1e;
|
||||
- }
|
||||
- ret = smapi_write(SMAPI_SET_CDROM_STATUS, cx, di, 0, &msg);
|
||||
- if (ret)
|
||||
- printk(TP_NOTICE "cannot set cd speed to %d: %s\n",
|
||||
- speed, msg);
|
||||
- else
|
||||
- printk(TP_INFO "cd speed set to level %d\n",
|
||||
- speed);
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-#endif /* PROVIDE_CD_SPEED */
|
||||
-
|
||||
@@ -1135,35 +1084,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
-/*********************************************************************
|
||||
- * Optical drive speed setting. This should be done by the ATAPI
|
||||
- * driver instead, and may get in the ATAPI driver's way.
|
||||
- */
|
||||
-
|
||||
-static int show_cd_speed(struct device *dev,
|
||||
- struct device_attribute *attr, char *buf)
|
||||
-{
|
||||
- int speed;
|
||||
- int ret = get_cd_speed(&speed);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- return sprintf(buf, "%d\n", speed);
|
||||
-}
|
||||
-
|
||||
-static int store_cd_speed(struct device *dev,
|
||||
- struct device_attribute *attr,
|
||||
- const char *buf, size_t count)
|
||||
-{
|
||||
- int ret;
|
||||
- int speed;
|
||||
- if (sscanf(buf, "%d", &speed)<1 || speed<0 || speed>2) {
|
||||
- printk(TP_ERR "cd_speed: must be between 0 and 2\n");
|
||||
- return -EINVAL;
|
||||
- }
|
||||
- ret = set_cd_speed(speed);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- return count;
|
||||
-}
|
||||
-
|
||||
-#endif /* PROVIDE_CD_SPEED */
|
||||
-
|
||||
-
|
||||
@@ -1235,3 +1149,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
-static DEVICE_ATTR(cd_speed, 0644, show_cd_speed, store_cd_speed);
|
||||
-#endif
|
||||
@@ -1243,3 +1154,0 @@
|
||||
-#ifdef PROVIDE_CD_SPEED
|
||||
- &dev_attr_cd_speed.attr,
|
||||
-#endif
|
172
hdaps.c
172
hdaps.c
@ -65,14 +65,14 @@ static struct timer_list hdaps_timer;
|
||||
static struct platform_device *pdev;
|
||||
static struct input_dev *hdaps_idev;
|
||||
static unsigned int hdaps_invert;
|
||||
static int needs_calibration = 0;
|
||||
static int needs_calibration;
|
||||
|
||||
/* Configuration: */
|
||||
static int sampling_rate = 50; /* Sampling rate */
|
||||
static int oversampling_ratio = 5; /* Ratio between our sampling rate and
|
||||
static int oversampling_ratio = 5; /* Ratio between our sampling rate and
|
||||
* EC accelerometer sampling rate */
|
||||
static int running_avg_filter_order = 2; /* EC running average filter order */
|
||||
static int fake_data_mode = 0; /* Enable EC fake data mode? */
|
||||
static int fake_data_mode; /* Enable EC fake data mode? */
|
||||
|
||||
/* Latest state readout: */
|
||||
static int pos_x, pos_y; /* position */
|
||||
@ -84,10 +84,21 @@ static int rest_x, rest_y; /* calibrated rest position */
|
||||
static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
|
||||
static u64 last_mouse_jiffies = INITIAL_JIFFIES;
|
||||
|
||||
/* __hdaps_update - read current state and update global state variables.
|
||||
* Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
* If fast!=0, do one quick attempt without retries.
|
||||
* Caller must hold controller lock.
|
||||
/* Some models require an axis transformation to the standard reprsentation */
|
||||
static void transform_axes(int *x, int *y)
|
||||
{
|
||||
if (hdaps_invert) {
|
||||
*x = -*x;
|
||||
*y = -*y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __hdaps_update - query current state, with locks already acquired
|
||||
* @fast: if nonzero, do one quick attempt without retries.
|
||||
*
|
||||
* Query current accelerometer state and update global state variables.
|
||||
* Also prefetches the next query. Caller must hold controller lock.
|
||||
*/
|
||||
static int __hdaps_update(int fast)
|
||||
{
|
||||
@ -113,12 +124,13 @@ static int __hdaps_update(int fast)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (data.val[EC_ACCEL_IDX_READOUTS]<1)
|
||||
if (data.val[EC_ACCEL_IDX_READOUTS] < 1)
|
||||
return -EBUSY; /* no pending readout, try again later */
|
||||
|
||||
/* Parse position data: */
|
||||
pos_x = *(s16*)(data.val+EC_ACCEL_IDX_XPOS1) * (hdaps_invert?-1:1);
|
||||
pos_y = *(s16*)(data.val+EC_ACCEL_IDX_YPOS1) * (hdaps_invert?-1:1);
|
||||
pos_x = *(s16*)(data.val+EC_ACCEL_IDX_XPOS1);
|
||||
pos_y = *(s16*)(data.val+EC_ACCEL_IDX_YPOS1);
|
||||
transform_axes(&pos_x, &pos_y);
|
||||
|
||||
/* Keyboard and mouse activity status is cleared as soon as it's read,
|
||||
* so applications will eat each other's events. Thus we remember any
|
||||
@ -141,8 +153,11 @@ static int __hdaps_update(int fast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hdaps_update - read current state and update global state variables.
|
||||
* Also prefetches the next read, to reduce udelay busy-waiting.
|
||||
/**
|
||||
* hdaps_update - acquire locks and query current state
|
||||
*
|
||||
* Query current accelerometer state and update global state variables.
|
||||
* Also prefetches the next query.
|
||||
* Retries until timeout if the accelerometer is not in ready status (common).
|
||||
* Does its own locking.
|
||||
*/
|
||||
@ -151,7 +166,7 @@ static int hdaps_update(void)
|
||||
int total, ret;
|
||||
if (!stale_readout) /* already updated recently? */
|
||||
return 0;
|
||||
for (total=READ_TIMEOUT_MSECS; total>0; total-=RETRY_MSECS) {
|
||||
for (total=0; total<READ_TIMEOUT_MSECS; total+=RETRY_MSECS) {
|
||||
ret = thinkpad_ec_lock();
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -167,12 +182,13 @@ static int hdaps_update(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* hdaps_set_power - enable or disable power to the accelerometer.
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_power(int on) {
|
||||
struct thinkpad_ec_row args =
|
||||
static int hdaps_set_power(int on)
|
||||
{
|
||||
struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x14, on?0x01:0x00} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
@ -183,13 +199,14 @@ static int hdaps_set_power(int on) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hdaps_set_fake_data_mode - enable or disable EC test mode, which fakes
|
||||
* accelerometer data using an incrementing counter.
|
||||
/**
|
||||
* hdaps_set_fake_data_mode - enable or disable EC test mode
|
||||
* EC test mode fakes accelerometer data using an incrementing counter.
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_fake_data_mode(int on) {
|
||||
struct thinkpad_ec_row args =
|
||||
static int hdaps_set_fake_data_mode(int on)
|
||||
{
|
||||
struct thinkpad_ec_row args =
|
||||
{ .mask=0x0007, .val={0x17, 0x83, on?0x01:0x00} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
@ -204,15 +221,16 @@ static int hdaps_set_fake_data_mode(int on) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* hdaps_set_ec_config - set accelerometer parameters.
|
||||
* ec_rate - embedded controller sampling rate
|
||||
* ( sampling_rate * oversampling_ratio )
|
||||
* order - embedded controller running average filter order
|
||||
* @ec_rate: embedded controller sampling rate
|
||||
* @order: embedded controller running average filter order
|
||||
* (Normally we have @ec_rate = sampling_rate * oversampling_ratio.)
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_set_ec_config(int ec_rate, int order) {
|
||||
struct thinkpad_ec_row args = { .mask=0x000F,
|
||||
static int hdaps_set_ec_config(int ec_rate, int order)
|
||||
{
|
||||
struct thinkpad_ec_row args = { .mask=0x000F,
|
||||
.val={0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x8000 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
@ -236,14 +254,15 @@ static int hdaps_set_ec_config(int ec_rate, int order) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* hdaps_get_ec_config - get accelerometer parameters.
|
||||
* ec_rate - embedded controller sampling rate
|
||||
* order - embedded controller running average filter order
|
||||
* @ec_rate: embedded controller sampling rate
|
||||
* @order: embedded controller running average filter order
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_get_ec_config(int *ec_rate, int *order) {
|
||||
const struct thinkpad_ec_row args =
|
||||
static int hdaps_get_ec_config(int *ec_rate, int *order)
|
||||
{
|
||||
const struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x17, 0x82} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x801F };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
@ -260,18 +279,19 @@ static int hdaps_get_ec_config(int *ec_rate, int *order) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* hdaps_get_ec_mode - get EC accelerometer mode
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int hdaps_get_ec_mode(u8 *mode) {
|
||||
static int hdaps_get_ec_mode(u8 *mode)
|
||||
{
|
||||
const struct thinkpad_ec_row args = { .mask=0x0001, .val={0x13} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x8002 };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data.val[0xF]!=0x00) {
|
||||
printk(KERN_WARNING
|
||||
printk(KERN_WARNING
|
||||
"accelerometer not implemented (0x%02x)\n",
|
||||
data.val[0xF]);
|
||||
return -EIO;
|
||||
@ -280,13 +300,14 @@ static int hdaps_get_ec_mode(u8 *mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* hdaps_check_ec - checks something about the EC.
|
||||
* Follows the clean-room spec for HDAPS; we don't know what it means.
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
static int __init hdaps_check_ec(u8 *mode) {
|
||||
const struct thinkpad_ec_row args =
|
||||
static int hdaps_check_ec()
|
||||
{
|
||||
const struct thinkpad_ec_row args =
|
||||
{ .mask=0x0003, .val={0x17, 0x81} };
|
||||
struct thinkpad_ec_row data = { .mask = 0x800E };
|
||||
int ret = thinkpad_ec_read_row(&args, &data);
|
||||
@ -298,11 +319,14 @@ static int __init hdaps_check_ec(u8 *mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hdaps_device_init - initialize the accelerometer. Returns zero on success
|
||||
* and negative error code on failure. Can sleep.
|
||||
/**
|
||||
* hdaps_device_init - initialize the accelerometer.
|
||||
*
|
||||
* Call several embedded controller functions to test and initialize the
|
||||
* accelerometer.
|
||||
* Returns zero on success and negative error code on failure. Can sleep.
|
||||
*/
|
||||
#define ABORT_INIT(msg) { printk(KERN_ERR "hdaps init: %s\n", msg); goto bad; }
|
||||
#define ABORT_INIT(msg) printk(KERN_ERR "hdaps init failed at: %s\n", msg)
|
||||
static int hdaps_device_init(void)
|
||||
{
|
||||
int ret;
|
||||
@ -313,23 +337,24 @@ static int hdaps_device_init(void)
|
||||
return ret;
|
||||
|
||||
if (hdaps_get_ec_mode(&mode))
|
||||
ABORT_INIT("hdaps_get_ec_mode failed");
|
||||
{ ABORT_INIT("hdaps_get_ec_mode failed"); goto bad; }
|
||||
|
||||
printk(KERN_DEBUG "hdaps: initial mode latch is 0x%02x\n", mode);
|
||||
if (mode==0x00)
|
||||
ABORT_INIT("accelerometer not available");
|
||||
{ ABORT_INIT("accelerometer not available"); goto bad; }
|
||||
|
||||
if (hdaps_check_ec(&mode))
|
||||
ABORT_INIT("hdaps_check_ec failed");
|
||||
if (hdaps_check_ec())
|
||||
{ ABORT_INIT("hdaps_check_ec failed"); goto bad; }
|
||||
|
||||
if (hdaps_set_power(1))
|
||||
ABORT_INIT("hdaps_set_power failed");
|
||||
{ ABORT_INIT("hdaps_set_power failed"); goto bad; }
|
||||
|
||||
if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
|
||||
running_avg_filter_order))
|
||||
ABORT_INIT("hdaps_set_ec_config failed");
|
||||
{ ABORT_INIT("hdaps_set_ec_config failed"); goto bad; }
|
||||
|
||||
if (hdaps_set_fake_data_mode(fake_data_mode))
|
||||
ABORT_INIT("hdaps_set_fake_data_mode failed");
|
||||
{ ABORT_INIT("hdaps_set_fake_data_mode failed"); goto bad; }
|
||||
|
||||
thinkpad_ec_invalidate();
|
||||
udelay(200);
|
||||
@ -337,7 +362,7 @@ static int hdaps_device_init(void)
|
||||
/* Just prefetch instead of reading, to avoid ~1sec delay on load */
|
||||
ret = thinkpad_ec_prefetch_row(&ec_accel_args);
|
||||
if (ret)
|
||||
ABORT_INIT("initial prefetch failed");
|
||||
{ ABORT_INIT("initial prefetch failed"); goto bad; }
|
||||
goto good;
|
||||
bad:
|
||||
thinkpad_ec_invalidate();
|
||||
@ -348,14 +373,22 @@ good:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* hdaps_device_shutdown - power off the accelerometer. Can sleep.
|
||||
/**
|
||||
* hdaps_device_shutdown - power off the accelerometer
|
||||
* Returns nonzero on failure. Can sleep.
|
||||
*/
|
||||
static void hdaps_device_shutdown(void) {
|
||||
if (hdaps_set_power(0))
|
||||
static int hdaps_device_shutdown(void)
|
||||
{
|
||||
int ret;
|
||||
ret = hdaps_set_power(0);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "hdaps: cannot power off\n");
|
||||
if (hdaps_set_ec_config(0, 1))
|
||||
return ret;
|
||||
}
|
||||
ret = hdaps_set_ec_config(0, 1);
|
||||
if (ret)
|
||||
printk(KERN_WARNING "hdaps: cannot stop EC sampling\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Device model stuff */
|
||||
@ -376,7 +409,7 @@ static int hdaps_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
/* Don't do hdaps polls until resume re-initializes the sensor. */
|
||||
del_timer_sync(&hdaps_timer);
|
||||
hdaps_device_shutdown();
|
||||
hdaps_device_shutdown(); /* ignore errors, effect is negligible */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -399,8 +432,9 @@ static struct platform_driver hdaps_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* hdaps_calibrate - Set our "resting" values. Does its own locking.
|
||||
/**
|
||||
* hdaps_calibrate - set our "resting" values.
|
||||
* Does its own locking.
|
||||
*/
|
||||
static void hdaps_calibrate(void)
|
||||
{
|
||||
@ -426,8 +460,8 @@ static void hdaps_mousedev_poll(unsigned long unused)
|
||||
thinkpad_ec_unlock();
|
||||
/* Any of "successful", "not yet ready" and "not prefetched"? */
|
||||
if (ret!=0 && ret!=-EBUSY && ret!=-ENODATA) {
|
||||
printk(KERN_ERR
|
||||
"hdaps: poll failed, disabling update timer\n");
|
||||
printk(KERN_ERR
|
||||
"hdaps: poll failed, disabling updates\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -467,7 +501,7 @@ static ssize_t hdaps_keyboard_activity_show(struct device *dev,
|
||||
int ret = hdaps_update();
|
||||
if (ret)
|
||||
return ret;
|
||||
return sprintf(buf, "%u\n",
|
||||
return sprintf(buf, "%u\n",
|
||||
get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD);
|
||||
}
|
||||
|
||||
@ -478,7 +512,7 @@ static ssize_t hdaps_mouse_activity_show(struct device *dev,
|
||||
int ret = hdaps_update();
|
||||
if (ret)
|
||||
return ret;
|
||||
return sprintf(buf, "%u\n",
|
||||
return sprintf(buf, "%u\n",
|
||||
get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD);
|
||||
}
|
||||
|
||||
@ -529,7 +563,7 @@ static ssize_t hdaps_sampling_rate_store(
|
||||
{
|
||||
int rate, ret;
|
||||
if (sscanf(buf, "%d", &rate) != 1 || rate>HZ || rate<0) {
|
||||
printk(KERN_WARNING
|
||||
printk(KERN_WARNING
|
||||
"must have 0<input_sampling_rate<=HZ=%d\n", HZ);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -558,7 +592,7 @@ static ssize_t hdaps_oversampling_ratio_store(
|
||||
int ratio, ret;
|
||||
if (sscanf(buf, "%d", &ratio) != 1 || ratio<1)
|
||||
return -EINVAL;
|
||||
ret = hdaps_set_ec_config(sampling_rate*ratio,
|
||||
ret = hdaps_set_ec_config(sampling_rate*ratio,
|
||||
running_avg_filter_order);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -595,11 +629,8 @@ static ssize_t hdaps_fake_data_mode_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int on, ret;
|
||||
if (sscanf(buf, "%d", &on) != 1 || on<0 || on>1) {
|
||||
printk(KERN_WARNING
|
||||
"fake_data should be 0 or 1\n");
|
||||
if (sscanf(buf, "%d", &on) != 1 || on<0 || on>1)
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = hdaps_set_fake_data_mode(on);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -656,7 +687,7 @@ static struct attribute_group hdaps_attribute_group = {
|
||||
static int hdaps_dmi_match_invert(struct dmi_system_id *id)
|
||||
{
|
||||
hdaps_invert = 1;
|
||||
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
|
||||
printk(KERN_INFO "hdaps: %s detected, inverting axes\n",
|
||||
id->ident);
|
||||
return 1;
|
||||
}
|
||||
@ -736,6 +767,7 @@ out_device:
|
||||
platform_device_unregister(pdev);
|
||||
out_driver:
|
||||
platform_driver_unregister(&hdaps_driver);
|
||||
hdaps_device_shutdown();
|
||||
out:
|
||||
printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
|
||||
return ret;
|
||||
@ -745,7 +777,7 @@ static void __exit hdaps_exit(void)
|
||||
{
|
||||
del_timer_sync(&hdaps_timer);
|
||||
input_unregister_device(hdaps_idev);
|
||||
hdaps_device_shutdown();
|
||||
hdaps_device_shutdown(); /* ignore errors, effect is negligible */
|
||||
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
|
||||
platform_device_unregister(pdev);
|
||||
platform_driver_unregister(&hdaps_driver);
|
||||
|
145
thinkpad_ec.c
145
thinkpad_ec.c
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* thinkpad_ec.c - coordinate access to ThinkPad-specific hardware resources
|
||||
*
|
||||
*
|
||||
* The embedded controller on ThinkPad laptops has a non-standard interface
|
||||
* at IO ports 0x1600-0x161F (mapped to LCP channel 3 of the H8S chip).
|
||||
* The interface provides various system management services (currently
|
||||
* 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:
|
||||
@ -36,7 +36,7 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define TP_VERSION "0.27"
|
||||
#define TP_VERSION "0.28"
|
||||
|
||||
MODULE_AUTHOR("Shem Multinymous");
|
||||
MODULE_DESCRIPTION("ThinkPad embedded controller hardware access");
|
||||
@ -65,14 +65,7 @@ MODULE_LICENSE("GPL");
|
||||
#define TPC_REQUEST_NDELAY 10
|
||||
#define TPC_PREFETCH_TIMEOUT (HZ/10) /* invalidate prefetch after 0.1sec */
|
||||
|
||||
/* Module parameters: */
|
||||
static int tp_debug = 0;
|
||||
module_param_named(debug, tp_debug, int, 0600);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0=off, 1=on)");
|
||||
|
||||
/* A few macros for printk()ing: */
|
||||
#define DPRINTK(fmt, args...) \
|
||||
do { if (tp_debug) printk(KERN_DEBUG fmt, ## args); } while (0)
|
||||
#define MSG_FMT(fmt, args...) \
|
||||
"thinkpad_ec: %s: " fmt "\n", __func__, ## args
|
||||
#define REQ_FMT(msg, code) \
|
||||
@ -89,36 +82,42 @@ static u64 prefetch_jiffies; /* time of prefetch, or: */
|
||||
|
||||
static DECLARE_MUTEX(thinkpad_ec_mutex);
|
||||
|
||||
/* thinkpad_ec_lock:
|
||||
* Get exclusive lock for accesing the controller. May sleep.
|
||||
* Returns 0 iff lock acquired .
|
||||
/**
|
||||
* thinkpad_ec_lock - get lock on the ThinkPad EC
|
||||
*
|
||||
* Get exclusive lock for accesing the ThinkPad embedded controller LPC3
|
||||
* interface. Returns 0 iff lock acquired.
|
||||
*/
|
||||
int thinkpad_ec_lock(void)
|
||||
int thinkpad_ec_lock(void)
|
||||
{
|
||||
int ret;
|
||||
ret = down_interruptible(&thinkpad_ec_mutex);
|
||||
if (ret)
|
||||
DPRINTK("tp_controller mutex down interrupted: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_lock);
|
||||
|
||||
/* thinkpad_ec_try_lock:
|
||||
* Get exclusive lock for accesing the controller but only if it's available.
|
||||
* Does not block, does not sleep. Returns 0 iff lock acquired .
|
||||
/**
|
||||
* thinkpad_ec_try_lock - try getting lock on the ThinkPad EC
|
||||
*
|
||||
* Try getting an exclusive lock for accesing the ThinkPad embedded
|
||||
* controller LPC3. Returns immediately if lock is not available; neither
|
||||
* blocks nor sleeps. Returns 0 iff lock acquired .
|
||||
*/
|
||||
int thinkpad_ec_try_lock(void)
|
||||
int thinkpad_ec_try_lock(void)
|
||||
{
|
||||
return down_trylock(&thinkpad_ec_mutex);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_try_lock);
|
||||
|
||||
/* thinkpad_ec_unlock:
|
||||
* Release a previously acquired controller lock.
|
||||
/**
|
||||
* thinkpad_ec_unlock - release lock on ThinkPad EC
|
||||
*
|
||||
* Release a previously acquired exclusive lock on the ThinkPad ebmedded
|
||||
* controller LPC3 interface.
|
||||
*/
|
||||
void thinkpad_ec_unlock(void)
|
||||
void thinkpad_ec_unlock(void)
|
||||
{
|
||||
up(&thinkpad_ec_mutex);
|
||||
}
|
||||
@ -126,7 +125,7 @@ void thinkpad_ec_unlock(void)
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_unlock);
|
||||
|
||||
/* Tell embedded controller to prepare a row */
|
||||
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
|
||||
static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
|
||||
{
|
||||
u8 str3;
|
||||
int i;
|
||||
@ -175,7 +174,7 @@ static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
|
||||
* Releasing locks before this happens may cause an EC hang
|
||||
* due to firmware bug!
|
||||
*/
|
||||
for (i=0; i<TPC_REQUEST_RETRIES; ++i) {
|
||||
for (i=0; i<TPC_REQUEST_RETRIES; i++) {
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 & H8S_STR3_SWMF) /* EC started replying */
|
||||
return 0;
|
||||
@ -193,7 +192,7 @@ static int thinkpad_ec_request_row(const struct thinkpad_ec_row *args)
|
||||
}
|
||||
|
||||
/* Read current row data from the controller, assuming it's already
|
||||
* requested.
|
||||
* requested.
|
||||
*/
|
||||
static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
|
||||
{
|
||||
@ -213,16 +212,16 @@ static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
|
||||
MSG_FMT("bad initial STR3 (0x%02x)", str3));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
||||
/* Read first byte (signals start of read transactions): */
|
||||
data->val[0] = inb(TPC_TWR0_PORT);
|
||||
/* Optionally read 14 more bytes: */
|
||||
for (i=1; i<TP_CONTROLLER_ROW_LEN-1; ++i)
|
||||
for (i=1; i<TP_CONTROLLER_ROW_LEN-1; i++)
|
||||
if ((data->mask >> i)&1)
|
||||
data->val[i] = inb(TPC_TWR0_PORT+i);
|
||||
/* Read last byte from 0x161F (signals end of read transaction): */
|
||||
data->val[0xF] = inb(TPC_TWR15_PORT);
|
||||
|
||||
|
||||
/* Readout still pending? */
|
||||
str3 = inb(TPC_STR3_PORT) & H8S_STR3_MASK;
|
||||
if (str3 & H8S_STR3_OBF3B)
|
||||
@ -231,7 +230,7 @@ static int thinkpad_ec_read_data(struct thinkpad_ec_row *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is the given row currently prefetched?
|
||||
/* Is the given row currently prefetched?
|
||||
* To keep things simple we compare only the first and last args;
|
||||
* in practice this suffices .*/
|
||||
static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
|
||||
@ -243,16 +242,22 @@ static int thinkpad_ec_is_row_fetched(const struct thinkpad_ec_row *args)
|
||||
(get_jiffies_64() < prefetch_jiffies + TPC_PREFETCH_TIMEOUT);
|
||||
}
|
||||
|
||||
/* thinkpad_ec_read_row:
|
||||
* Read a data row from the controller, fetching and retrying if needed.
|
||||
* The row args are specified by 16 byte arguments, some of which may be
|
||||
* missing (but the first and last are mandatory). These are given in
|
||||
* args->val[], where args->val[i] is used iff (args->mask>>i)&1).
|
||||
* The rows's data is stored in data->val[], but is only guaranteed to be
|
||||
* valid for indices corresponding to set bit in data->maska. That is,
|
||||
* if (data->mask>>i)&1==0 then data->val[i] may not be filled (to save time).
|
||||
/**
|
||||
* thinkpad_ec_read_row - request and read data from ThinkPad EC
|
||||
* @args Input register arguments
|
||||
* @data Output register values
|
||||
*
|
||||
* Read a data row from the ThinkPad embedded controller LPC3 interface.
|
||||
* Does fetching and retrying if needed. The row args are specified by
|
||||
* 16 byte arguments, some of which may be missing (but the first is
|
||||
* mandatory). These are given in @args->val[], where @args->val[i] is
|
||||
* used iff (@args->mask>>i)&1). The rows's data is stored in @data->val[],
|
||||
* but is only guaranteed to be valid for indices corresponding to set
|
||||
* bit in @data->mask. That is, if (@data->mask>>i)&1==0 then @data->val[i]
|
||||
* may not be filled (to save time).
|
||||
*
|
||||
* Returns -EBUSY on transient error and -EIO on abnormal condition.
|
||||
* Caller must hold controller lock.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
int thinkpad_ec_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *data)
|
||||
@ -294,11 +299,18 @@ out:
|
||||
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_read_row);
|
||||
|
||||
/* thinkpad_ec_try_read_row:
|
||||
* Try read a prefetched row from the controller. Don't fetch or retry.
|
||||
* See thinkpad_ec_read_row above for the meaning of the arguments.
|
||||
/**
|
||||
* thinkpad_ec_try_read_row - try reading prefetched data from ThinkPad EC
|
||||
* @args Input register arguments
|
||||
* @data Output register values
|
||||
*
|
||||
* Try reading a data row from the ThinkPad embedded controller LPC3
|
||||
* interface, if this raw was recently prefetched using
|
||||
* thinkpad_ec_prefetch_row(). Does not fetch, retry or block.
|
||||
* The parameters have the same meaning as in thinkpad_ec_read_row().
|
||||
*
|
||||
* Returns -EBUSY is data not ready and -ENODATA if row not prefetched.
|
||||
* Caller must hold controller lock.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
|
||||
struct thinkpad_ec_row *data)
|
||||
@ -316,12 +328,17 @@ int thinkpad_ec_try_read_row(const struct thinkpad_ec_row *args,
|
||||
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_try_read_row);
|
||||
|
||||
/* thinkpad_ec_prefetch_row:
|
||||
* Prefetch data row from the controller. A subsequent call to
|
||||
* thinkpad_ec_read_row() with the same arguments will be faster,
|
||||
* and a subsequent call to thinkpad_ec_try_read_row stands a
|
||||
* good chance of succeeding if done neither too soon nor too late.
|
||||
* See thinkpad_ec_read_row above for the meaning of the arguments.
|
||||
/**
|
||||
* thinkpad_ec_prefetch_row - prefetch data from ThinkPad EC
|
||||
* @args Input register arguments
|
||||
*
|
||||
* Prefetch a data row from the ThinkPad embedded controller LCP3
|
||||
* interface. A subsequent call to thinkpad_ec_read_row() with the
|
||||
* same arguments will be faster, and a subsequent call to
|
||||
* thinkpad_ec_try_read_row() stands a good chance of succeeding if
|
||||
* done neither too soon nor too late. See
|
||||
* thinkpad_ec_read_row() for the meaning of @args.
|
||||
*
|
||||
* Returns -EBUSY on transient error and -EIO on abnormal condition.
|
||||
* Caller must hold controller lock.
|
||||
*/
|
||||
@ -341,12 +358,15 @@ int thinkpad_ec_prefetch_row(const struct thinkpad_ec_row *args)
|
||||
|
||||
EXPORT_SYMBOL_GPL(thinkpad_ec_prefetch_row);
|
||||
|
||||
/* thinkpad_ec_invalidate:
|
||||
* Invalidate the prefetched controller data.
|
||||
/**
|
||||
* thinkpad_ec_invalidate - invalidate prefetched ThinkPad EC data
|
||||
*
|
||||
* Invalidate the data prefetched via thinkpad_ec_prefetch_row() from the
|
||||
* ThinkPad embedded controller LPC3 interface.
|
||||
* Must be called before unlocking by any code that accesses the controller
|
||||
* ports directly.
|
||||
*/
|
||||
void thinkpad_ec_invalidate(void)
|
||||
void thinkpad_ec_invalidate(void)
|
||||
{
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
}
|
||||
@ -361,7 +381,8 @@ EXPORT_SYMBOL_GPL(thinkpad_ec_invalidate);
|
||||
* an arbitrary harmless EC request and seeing if the EC follows protocol.
|
||||
* This test writes to IO ports, so execute only after checking DMI.
|
||||
*/
|
||||
static int thinkpad_ec_test(void) {
|
||||
static int thinkpad_ec_test(void)
|
||||
{
|
||||
int ret;
|
||||
const struct thinkpad_ec_row args = /* battery 0 basic status */
|
||||
{ .mask=0x8001, .val={0x01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x00} };
|
||||
@ -374,8 +395,9 @@ static int thinkpad_ec_test(void) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Search all DMI device names for a given type for a substrng */
|
||||
static int __init dmi_find_substring(int type, const char *substr) {
|
||||
/* Search all DMI device names of a given type for a substring */
|
||||
static int __init dmi_find_substring(int type, const char *substr)
|
||||
{
|
||||
struct dmi_device *dev = NULL;
|
||||
while ((dev = dmi_find_device(type, NULL, dev))) {
|
||||
if (strstr(dev->name, substr))
|
||||
@ -393,7 +415,7 @@ static int __init dmi_find_substring(int type, const char *substr) {
|
||||
}
|
||||
|
||||
/* Check DMI for existence of ThinkPad embedded controller */
|
||||
static int __init check_dmi_for_ec(void)
|
||||
static int __init check_dmi_for_ec(void)
|
||||
{
|
||||
/* A few old models that have a good EC but don't report it in DMI */
|
||||
struct dmi_system_id tp_whitelist[] = {
|
||||
@ -417,13 +439,12 @@ static int __init check_dmi_for_ec(void)
|
||||
static int __init thinkpad_ec_init(void)
|
||||
{
|
||||
if (!check_dmi_for_ec()) {
|
||||
printk(KERN_ERR "thinkpad_ec: no ThinkPad embedded controller!\n");
|
||||
printk(KERN_WARNING "thinkpad_ec: no ThinkPad embedded controller!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!request_region(TPC_BASE_PORT, TPC_NUM_PORTS,
|
||||
"thinkpad_ec"))
|
||||
{
|
||||
"thinkpad_ec")) {
|
||||
printk(KERN_ERR "thinkpad_ec: cannot claim io ports %#x-%#x\n",
|
||||
TPC_BASE_PORT,
|
||||
TPC_BASE_PORT + TPC_NUM_PORTS -1);
|
||||
@ -431,7 +452,7 @@ static int __init thinkpad_ec_init(void)
|
||||
}
|
||||
prefetch_jiffies = TPC_PREFETCH_JUNK;
|
||||
if (thinkpad_ec_test()) {
|
||||
printk(KERN_INFO "thinkpad_ec: initial ec test failed\n");
|
||||
printk(KERN_ERR "thinkpad_ec: initial ec test failed\n");
|
||||
release_region(TPC_BASE_PORT, TPC_NUM_PORTS);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
1095
tp_smapi.c
1095
tp_smapi.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user