2 * drivers/platform/x86/hdaps.c - driver for IBM's Hard Drive Active Protection System
4 * Copyright (C) 2005 Robert Love <rml@novell.com>
5 * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
7 * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads
8 * starting with the R40, T41, and X40. It provides a basic two-axis
9 * accelerometer and other data, such as the device's temperature.
11 * This driver is based on the document by Mark A. Smith available at
12 * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License v2 as published by the
17 * Free Software Foundation.
19 * This program is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
24 * You should have received a copy of the GNU General Public License along with
25 * this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
29 #include <linux/delay.h>
30 #include <linux/platform_device.h>
31 #include <linux/input.h>
32 #include <linux/kernel.h>
33 #include <linux/module.h>
34 #include <linux/timer.h>
35 #include <linux/dmi.h>
36 #include <linux/jiffies.h>
37 #include "thinkpad_ec.h"
38 #include <linux/pci_ids.h>
39 #include <linux/version.h>
41 /* Embedded controller accelerometer read command and its result: */
42 static const struct thinkpad_ec_row ec_accel_args =
43 { .mask = 0x0001, .val = {0x11} };
44 #define EC_ACCEL_IDX_READOUTS 0x1 /* readouts included in this read */
45 /* First readout, if READOUTS>=1: */
46 #define EC_ACCEL_IDX_YPOS1 0x2 /* y-axis position word */
47 #define EC_ACCEL_IDX_XPOS1 0x4 /* x-axis position word */
48 #define EC_ACCEL_IDX_TEMP1 0x6 /* device temperature in Celsius */
49 /* Second readout, if READOUTS>=2: */
50 #define EC_ACCEL_IDX_XPOS2 0x7 /* y-axis position word */
51 #define EC_ACCEL_IDX_YPOS2 0x9 /* x-axis position word */
52 #define EC_ACCEL_IDX_TEMP2 0xb /* device temperature in Celsius */
53 #define EC_ACCEL_IDX_QUEUED 0xc /* Number of queued readouts left */
54 #define EC_ACCEL_IDX_KMACT 0xd /* keyboard or mouse activity */
55 #define EC_ACCEL_IDX_RETVAL 0xf /* command return value, good=0x00 */
57 #define KEYBD_MASK 0x20 /* set if keyboard activity */
58 #define MOUSE_MASK 0x40 /* set if mouse activity */
60 #define READ_TIMEOUT_MSECS 100 /* wait this long for device read */
61 #define RETRY_MSECS 3 /* retry delay */
63 #define HDAPS_INPUT_FUZZ 4 /* input event threshold */
64 #define HDAPS_INPUT_FLAT 4
65 #define KMACT_REMEMBER_PERIOD (HZ/10) /* keyboard/mouse persistance */
68 #define HDAPS_INPUT_VENDOR PCI_VENDOR_ID_IBM
69 #define HDAPS_INPUT_PRODUCT 0x5054 /* "TP", shared with thinkpad_acpi */
70 #define HDAPS_INPUT_JS_VERSION 0x6801 /* Joystick emulation input device */
71 #define HDAPS_INPUT_RAW_VERSION 0x4801 /* Raw accelerometer input device */
73 /* Axis orientation. */
74 /* The unnatural bit-representation of inversions is for backward
75 * compatibility with the"invert=1" module parameter. */
76 #define HDAPS_ORIENT_INVERT_XY 0x01 /* Invert both X and Y axes. */
77 #define HDAPS_ORIENT_INVERT_X 0x02 /* Invert the X axis (uninvert if
78 * already inverted by INVERT_XY). */
79 #define HDAPS_ORIENT_SWAP 0x04 /* Swap the axes. The swap occurs
80 * before inverting X or Y. */
81 #define HDAPS_ORIENT_MAX 0x07
82 #define HDAPS_ORIENT_UNDEFINED 0xFF /* Placeholder during initialization */
83 #define HDAPS_ORIENT_INVERT_Y (HDAPS_ORIENT_INVERT_XY | HDAPS_ORIENT_INVERT_X)
85 static struct timer_list hdaps_timer;
86 static struct platform_device *pdev;
87 static struct input_dev *hdaps_idev; /* joystick-like device with fuzz */
88 static struct input_dev *hdaps_idev_raw; /* raw hdaps sensor readouts */
89 static unsigned int hdaps_invert = HDAPS_ORIENT_UNDEFINED;
90 static int needs_calibration;
93 static int sampling_rate = 50; /* Sampling rate */
94 static int oversampling_ratio = 5; /* Ratio between our sampling rate and
95 * EC accelerometer sampling rate */
96 static int running_avg_filter_order = 2; /* EC running average filter order */
98 /* Latest state readout: */
99 static int pos_x, pos_y; /* position */
100 static int temperature; /* temperature */
101 static int stale_readout = 1; /* last read invalid */
102 static int rest_x, rest_y; /* calibrated rest position */
104 /* Last time we saw keyboard and mouse activity: */
105 static u64 last_keyboard_jiffies = INITIAL_JIFFIES;
106 static u64 last_mouse_jiffies = INITIAL_JIFFIES;
107 static u64 last_update_jiffies = INITIAL_JIFFIES;
109 /* input device use count */
110 static int hdaps_users;
111 static DEFINE_MUTEX(hdaps_users_mtx);
113 /* Some models require an axis transformation to the standard representation */
114 static void transform_axes(int *x, int *y)
116 if (hdaps_invert & HDAPS_ORIENT_SWAP) {
122 if (hdaps_invert & HDAPS_ORIENT_INVERT_XY) {
126 if (hdaps_invert & HDAPS_ORIENT_INVERT_X)
131 * __hdaps_update - query current state, with locks already acquired
132 * @fast: if nonzero, do one quick attempt without retries.
134 * Query current accelerometer state and update global state variables.
135 * Also prefetches the next query. Caller must hold controller lock.
137 static int __hdaps_update(int fast)
140 struct thinkpad_ec_row data;
143 data.mask = (1 << EC_ACCEL_IDX_READOUTS) | (1 << EC_ACCEL_IDX_KMACT) |
144 (3 << EC_ACCEL_IDX_YPOS1) | (3 << EC_ACCEL_IDX_XPOS1) |
145 (1 << EC_ACCEL_IDX_TEMP1) | (1 << EC_ACCEL_IDX_RETVAL);
147 ret = thinkpad_ec_try_read_row(&ec_accel_args, &data);
149 ret = thinkpad_ec_read_row(&ec_accel_args, &data);
150 thinkpad_ec_prefetch_row(&ec_accel_args); /* Prefetch even if error */
155 if (data.val[EC_ACCEL_IDX_RETVAL] != 0x00) {
156 printk(KERN_WARNING "hdaps: read RETVAL=0x%02x\n",
157 data.val[EC_ACCEL_IDX_RETVAL]);
161 if (data.val[EC_ACCEL_IDX_READOUTS] < 1)
162 return -EBUSY; /* no pending readout, try again later */
164 /* Parse position data: */
165 pos_x = *(s16 *)(data.val+EC_ACCEL_IDX_XPOS1);
166 pos_y = *(s16 *)(data.val+EC_ACCEL_IDX_YPOS1);
167 transform_axes(&pos_x, &pos_y);
169 /* Keyboard and mouse activity status is cleared as soon as it's read,
170 * so applications will eat each other's events. Thus we remember any
171 * event for KMACT_REMEMBER_PERIOD jiffies.
173 if (data.val[EC_ACCEL_IDX_KMACT] & KEYBD_MASK)
174 last_keyboard_jiffies = get_jiffies_64();
175 if (data.val[EC_ACCEL_IDX_KMACT] & MOUSE_MASK)
176 last_mouse_jiffies = get_jiffies_64();
178 temperature = data.val[EC_ACCEL_IDX_TEMP1];
180 last_update_jiffies = get_jiffies_64();
182 if (needs_calibration) {
185 needs_calibration = 0;
192 * hdaps_update - acquire locks and query current state
194 * Query current accelerometer state and update global state variables.
195 * Also prefetches the next query.
196 * Retries until timeout if the accelerometer is not in ready status (common).
197 * Does its own locking.
199 static int hdaps_update(void)
201 u64 age = get_jiffies_64() - last_update_jiffies;
204 if (!stale_readout && age < (9*HZ)/(10*sampling_rate))
205 return 0; /* already updated recently */
206 for (total = 0; total < READ_TIMEOUT_MSECS; total += RETRY_MSECS) {
207 ret = thinkpad_ec_lock();
210 ret = __hdaps_update(0);
211 thinkpad_ec_unlock();
223 * hdaps_set_power - enable or disable power to the accelerometer.
224 * Returns zero on success and negative error code on failure. Can sleep.
226 static int hdaps_set_power(int on)
228 struct thinkpad_ec_row args =
229 { .mask = 0x0003, .val = {0x14, on?0x01:0x00} };
230 struct thinkpad_ec_row data = { .mask = 0x8000 };
231 int ret = thinkpad_ec_read_row(&args, &data);
234 if (data.val[0xF] != 0x00)
240 * hdaps_set_ec_config - set accelerometer parameters.
241 * @ec_rate: embedded controller sampling rate
242 * @order: embedded controller running average filter order
243 * (Normally we have @ec_rate = sampling_rate * oversampling_ratio.)
244 * Returns zero on success and negative error code on failure. Can sleep.
246 static int hdaps_set_ec_config(int ec_rate, int order)
248 struct thinkpad_ec_row args = { .mask = 0x000F,
249 .val = {0x10, (u8)ec_rate, (u8)(ec_rate>>8), order} };
250 struct thinkpad_ec_row data = { .mask = 0x8000 };
251 int ret = thinkpad_ec_read_row(&args, &data);
252 printk(KERN_DEBUG "hdaps: setting ec_rate=%d, filter_order=%d\n",
256 if (data.val[0xF] == 0x03) {
257 printk(KERN_WARNING "hdaps: config param out of range\n");
260 if (data.val[0xF] == 0x06) {
261 printk(KERN_WARNING "hdaps: config change already pending\n");
264 if (data.val[0xF] != 0x00) {
265 printk(KERN_WARNING "hdaps: config change error, ret=%d\n",
273 * hdaps_get_ec_config - get accelerometer parameters.
274 * @ec_rate: embedded controller sampling rate
275 * @order: embedded controller running average filter order
276 * Returns zero on success and negative error code on failure. Can sleep.
278 static int hdaps_get_ec_config(int *ec_rate, int *order)
280 const struct thinkpad_ec_row args =
281 { .mask = 0x0003, .val = {0x17, 0x82} };
282 struct thinkpad_ec_row data = { .mask = 0x801F };
283 int ret = thinkpad_ec_read_row(&args, &data);
286 if (data.val[0xF] != 0x00)
288 if (!(data.val[0x1] & 0x01))
289 return -ENXIO; /* accelerometer polling not enabled */
290 if (data.val[0x1] & 0x02)
291 return -EBUSY; /* config change in progress, retry later */
292 *ec_rate = data.val[0x2] | ((int)(data.val[0x3]) << 8);
293 *order = data.val[0x4];
298 * hdaps_get_ec_mode - get EC accelerometer mode
299 * Returns zero on success and negative error code on failure. Can sleep.
301 static int hdaps_get_ec_mode(u8 *mode)
303 const struct thinkpad_ec_row args =
304 { .mask = 0x0001, .val = {0x13} };
305 struct thinkpad_ec_row data = { .mask = 0x8002 };
306 int ret = thinkpad_ec_read_row(&args, &data);
309 if (data.val[0xF] != 0x00) {
311 "accelerometer not implemented (0x%02x)\n",
315 *mode = data.val[0x1];
320 * hdaps_check_ec - checks something about the EC.
321 * Follows the clean-room spec for HDAPS; we don't know what it means.
322 * Returns zero on success and negative error code on failure. Can sleep.
324 static int hdaps_check_ec(void)
326 const struct thinkpad_ec_row args =
327 { .mask = 0x0003, .val = {0x17, 0x81} };
328 struct thinkpad_ec_row data = { .mask = 0x800E };
329 int ret = thinkpad_ec_read_row(&args, &data);
332 if (!((data.val[0x1] == 0x00 && data.val[0x2] == 0x60) || /* cleanroom spec */
333 (data.val[0x1] == 0x01 && data.val[0x2] == 0x00)) || /* seen on T61 */
334 data.val[0x3] != 0x00 || data.val[0xF] != 0x00) {
336 "hdaps_check_ec: bad response (0x%x,0x%x,0x%x,0x%x)\n",
337 data.val[0x1], data.val[0x2],
338 data.val[0x3], data.val[0xF]);
345 * hdaps_device_init - initialize the accelerometer.
347 * Call several embedded controller functions to test and initialize the
349 * Returns zero on success and negative error code on failure. Can sleep.
351 #define FAILED_INIT(msg) printk(KERN_ERR "hdaps init failed at: %s\n", msg)
352 static int hdaps_device_init(void)
357 ret = thinkpad_ec_lock();
361 if (hdaps_get_ec_mode(&mode))
362 { FAILED_INIT("hdaps_get_ec_mode failed"); goto bad; }
364 printk(KERN_DEBUG "hdaps: initial mode latch is 0x%02x\n", mode);
366 { FAILED_INIT("accelerometer not available"); goto bad; }
368 if (hdaps_check_ec())
369 { FAILED_INIT("hdaps_check_ec failed"); goto bad; }
371 if (hdaps_set_power(1))
372 { FAILED_INIT("hdaps_set_power failed"); goto bad; }
374 if (hdaps_set_ec_config(sampling_rate*oversampling_ratio,
375 running_avg_filter_order))
376 { FAILED_INIT("hdaps_set_ec_config failed"); goto bad; }
378 thinkpad_ec_invalidate();
381 /* Just prefetch instead of reading, to avoid ~1sec delay on load */
382 ret = thinkpad_ec_prefetch_row(&ec_accel_args);
384 { FAILED_INIT("initial prefetch failed"); goto bad; }
387 thinkpad_ec_invalidate();
391 thinkpad_ec_unlock();
396 * hdaps_device_shutdown - power off the accelerometer
397 * Returns nonzero on failure. Can sleep.
399 static int hdaps_device_shutdown(void)
402 ret = hdaps_set_power(0);
404 printk(KERN_WARNING "hdaps: cannot power off\n");
407 ret = hdaps_set_ec_config(0, 1);
409 printk(KERN_WARNING "hdaps: cannot stop EC sampling\n");
413 /* Device model stuff */
415 static int hdaps_probe(struct platform_device *dev)
419 ret = hdaps_device_init();
423 printk(KERN_INFO "hdaps: device successfully initialized.\n");
427 static int hdaps_suspend(struct platform_device *dev, pm_message_t state)
429 /* Don't do hdaps polls until resume re-initializes the sensor. */
430 del_timer_sync(&hdaps_timer);
431 hdaps_device_shutdown(); /* ignore errors, effect is negligible */
435 static int hdaps_resume(struct platform_device *dev)
437 int ret = hdaps_device_init();
441 mutex_lock(&hdaps_users_mtx);
443 mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
444 mutex_unlock(&hdaps_users_mtx);
448 static struct platform_driver hdaps_driver = {
449 .probe = hdaps_probe,
450 .suspend = hdaps_suspend,
451 .resume = hdaps_resume,
454 .owner = THIS_MODULE,
459 * hdaps_calibrate - set our "resting" values.
460 * Does its own locking.
462 static void hdaps_calibrate(void)
464 needs_calibration = 1;
466 /* If that fails, the mousedev poll will take care of things later. */
469 /* Timer handler for updating the input device. Runs in softirq context,
470 * so avoid lenghty or blocking operations.
472 static void hdaps_mousedev_poll(unsigned long unused)
478 /* Cannot sleep. Try nonblockingly. If we fail, try again later. */
479 if (thinkpad_ec_try_lock())
482 ret = __hdaps_update(1); /* fast update, we're in softirq context */
483 thinkpad_ec_unlock();
484 /* Any of "successful", "not yet ready" and "not prefetched"? */
485 if (ret != 0 && ret != -EBUSY && ret != -ENODATA) {
487 "hdaps: poll failed, disabling updates\n");
492 /* Even if we failed now, pos_x,y may have been updated earlier: */
493 input_report_abs(hdaps_idev, ABS_X, pos_x - rest_x);
494 input_report_abs(hdaps_idev, ABS_Y, pos_y - rest_y);
495 input_sync(hdaps_idev);
496 input_report_abs(hdaps_idev_raw, ABS_X, pos_x);
497 input_report_abs(hdaps_idev_raw, ABS_Y, pos_y);
498 input_sync(hdaps_idev_raw);
499 mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
505 static ssize_t hdaps_position_show(struct device *dev,
506 struct device_attribute *attr, char *buf)
508 int ret = hdaps_update();
511 return sprintf(buf, "(%d,%d)\n", pos_x, pos_y);
514 static ssize_t hdaps_temp1_show(struct device *dev,
515 struct device_attribute *attr, char *buf)
517 int ret = hdaps_update();
520 return sprintf(buf, "%d\n", temperature);
523 static ssize_t hdaps_keyboard_activity_show(struct device *dev,
524 struct device_attribute *attr,
527 int ret = hdaps_update();
530 return sprintf(buf, "%u\n",
531 get_jiffies_64() < last_keyboard_jiffies + KMACT_REMEMBER_PERIOD);
534 static ssize_t hdaps_mouse_activity_show(struct device *dev,
535 struct device_attribute *attr,
538 int ret = hdaps_update();
541 return sprintf(buf, "%u\n",
542 get_jiffies_64() < last_mouse_jiffies + KMACT_REMEMBER_PERIOD);
545 static ssize_t hdaps_calibrate_show(struct device *dev,
546 struct device_attribute *attr, char *buf)
548 return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
551 static ssize_t hdaps_calibrate_store(struct device *dev,
552 struct device_attribute *attr,
553 const char *buf, size_t count)
559 static ssize_t hdaps_invert_show(struct device *dev,
560 struct device_attribute *attr, char *buf)
562 return sprintf(buf, "%u\n", hdaps_invert);
565 static ssize_t hdaps_invert_store(struct device *dev,
566 struct device_attribute *attr,
567 const char *buf, size_t count)
571 if (sscanf(buf, "%d", &invert) != 1 ||
572 invert < 0 || invert > HDAPS_ORIENT_MAX)
575 hdaps_invert = invert;
581 static ssize_t hdaps_sampling_rate_show(
582 struct device *dev, struct device_attribute *attr, char *buf)
584 return sprintf(buf, "%d\n", sampling_rate);
587 static ssize_t hdaps_sampling_rate_store(
588 struct device *dev, struct device_attribute *attr,
589 const char *buf, size_t count)
592 if (sscanf(buf, "%d", &rate) != 1 || rate > HZ || rate <= 0) {
594 "must have 0<input_sampling_rate<=HZ=%d\n", HZ);
597 ret = hdaps_set_ec_config(rate*oversampling_ratio,
598 running_avg_filter_order);
601 sampling_rate = rate;
605 static ssize_t hdaps_oversampling_ratio_show(
606 struct device *dev, struct device_attribute *attr, char *buf)
609 int ret = hdaps_get_ec_config(&ec_rate, &order);
612 return sprintf(buf, "%u\n", ec_rate / sampling_rate);
615 static ssize_t hdaps_oversampling_ratio_store(
616 struct device *dev, struct device_attribute *attr,
617 const char *buf, size_t count)
620 if (sscanf(buf, "%d", &ratio) != 1 || ratio < 1)
622 ret = hdaps_set_ec_config(sampling_rate*ratio,
623 running_avg_filter_order);
626 oversampling_ratio = ratio;
630 static ssize_t hdaps_running_avg_filter_order_show(
631 struct device *dev, struct device_attribute *attr, char *buf)
634 int ret = hdaps_get_ec_config(&rate, &order);
637 return sprintf(buf, "%u\n", order);
640 static ssize_t hdaps_running_avg_filter_order_store(
641 struct device *dev, struct device_attribute *attr,
642 const char *buf, size_t count)
645 if (sscanf(buf, "%d", &order) != 1)
647 ret = hdaps_set_ec_config(sampling_rate*oversampling_ratio, order);
650 running_avg_filter_order = order;
654 static int hdaps_mousedev_open(struct input_dev *dev)
656 if (!try_module_get(THIS_MODULE))
659 mutex_lock(&hdaps_users_mtx);
660 if (hdaps_users++ == 0) /* first input user */
661 mod_timer(&hdaps_timer, jiffies + HZ/sampling_rate);
662 mutex_unlock(&hdaps_users_mtx);
666 static void hdaps_mousedev_close(struct input_dev *dev)
668 mutex_lock(&hdaps_users_mtx);
669 if (--hdaps_users == 0) /* no input users left */
670 del_timer_sync(&hdaps_timer);
671 mutex_unlock(&hdaps_users_mtx);
673 module_put(THIS_MODULE);
676 static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
677 static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
678 /* "temp1" instead of "temperature" is hwmon convention */
679 static DEVICE_ATTR(keyboard_activity, 0444,
680 hdaps_keyboard_activity_show, NULL);
681 static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
682 static DEVICE_ATTR(calibrate, 0644,
683 hdaps_calibrate_show, hdaps_calibrate_store);
684 static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
685 static DEVICE_ATTR(sampling_rate, 0644,
686 hdaps_sampling_rate_show, hdaps_sampling_rate_store);
687 static DEVICE_ATTR(oversampling_ratio, 0644,
688 hdaps_oversampling_ratio_show,
689 hdaps_oversampling_ratio_store);
690 static DEVICE_ATTR(running_avg_filter_order, 0644,
691 hdaps_running_avg_filter_order_show,
692 hdaps_running_avg_filter_order_store);
694 static struct attribute *hdaps_attributes[] = {
695 &dev_attr_position.attr,
696 &dev_attr_temp1.attr,
697 &dev_attr_keyboard_activity.attr,
698 &dev_attr_mouse_activity.attr,
699 &dev_attr_calibrate.attr,
700 &dev_attr_invert.attr,
701 &dev_attr_sampling_rate.attr,
702 &dev_attr_oversampling_ratio.attr,
703 &dev_attr_running_avg_filter_order.attr,
707 static struct attribute_group hdaps_attribute_group = {
708 .attrs = hdaps_attributes,
714 /* hdaps_dmi_match_invert - found an inverted match. */
715 static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
717 unsigned int orient = (kernel_ulong_t) id->driver_data;
718 hdaps_invert = orient;
719 printk(KERN_INFO "hdaps: %s detected, setting orientation %u\n",
721 return 1; /* stop enumeration */
724 #define HDAPS_DMI_MATCH_INVERT(vendor, model, orient) { \
725 .ident = vendor " " model, \
726 .callback = hdaps_dmi_match_invert, \
727 .driver_data = (void *)(orient), \
729 DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
730 DMI_MATCH(DMI_PRODUCT_VERSION, model) \
734 /* List of models with abnormal axis configuration.
735 Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
736 "ThinkPad T42p", and enumeration stops after first match,
737 so the order of the entries matters. */
738 struct dmi_system_id __initconst hdaps_whitelist[] = {
739 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_ORIENT_INVERT_XY),
740 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
741 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_ORIENT_INVERT_XY),
742 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_ORIENT_INVERT_XY),
743 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X40", HDAPS_ORIENT_INVERT_Y),
744 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_ORIENT_INVERT_Y),
745 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R60", HDAPS_ORIENT_INVERT_XY),
746 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_ORIENT_INVERT_XY),
747 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R400", HDAPS_ORIENT_INVERT_XY),
748 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R500", HDAPS_ORIENT_INVERT_XY),
749 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_ORIENT_INVERT_XY),
750 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_ORIENT_INVERT_XY),
751 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60 Tablet", HDAPS_ORIENT_INVERT_Y),
752 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60s", HDAPS_ORIENT_INVERT_Y),
753 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
754 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
755 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400s", HDAPS_ORIENT_INVERT_X),
756 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_ORIENT_INVERT_XY),
757 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410s", HDAPS_ORIENT_SWAP),
758 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T410", HDAPS_ORIENT_INVERT_XY),
759 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T500", HDAPS_ORIENT_INVERT_XY),
760 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T510", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y),
761 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W510", HDAPS_ORIENT_MAX),
762 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad W520", HDAPS_ORIENT_MAX),
763 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
764 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X200", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X | HDAPS_ORIENT_INVERT_Y),
765 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201 Tablet", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
766 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201s", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_XY),
767 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X201", HDAPS_ORIENT_SWAP | HDAPS_ORIENT_INVERT_X),
768 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X220", HDAPS_ORIENT_SWAP),
772 static int __init hdaps_init(void)
776 /* Determine axis orientation orientation */
777 if (hdaps_invert == HDAPS_ORIENT_UNDEFINED) /* set by module param? */
778 if (dmi_check_system(hdaps_whitelist) < 1) /* in whitelist? */
779 hdaps_invert = 0; /* default */
781 /* Init timer before platform_driver_register, in case of suspend */
782 init_timer(&hdaps_timer);
783 hdaps_timer.function = hdaps_mousedev_poll;
784 ret = platform_driver_register(&hdaps_driver);
788 pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
794 ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
798 hdaps_idev = input_allocate_device();
804 hdaps_idev_raw = input_allocate_device();
805 if (!hdaps_idev_raw) {
810 /* calibration for the input device (deferred to avoid delay) */
811 needs_calibration = 1;
813 /* initialize the joystick-like fuzzed input device */
814 hdaps_idev->name = "ThinkPad HDAPS joystick emulation";
815 hdaps_idev->phys = "hdaps/input0";
816 hdaps_idev->id.bustype = BUS_HOST;
817 hdaps_idev->id.vendor = HDAPS_INPUT_VENDOR;
818 hdaps_idev->id.product = HDAPS_INPUT_PRODUCT;
819 hdaps_idev->id.version = HDAPS_INPUT_JS_VERSION;
820 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
821 hdaps_idev->cdev.dev = &pdev->dev;
823 hdaps_idev->evbit[0] = BIT(EV_ABS);
824 hdaps_idev->open = hdaps_mousedev_open;
825 hdaps_idev->close = hdaps_mousedev_close;
826 input_set_abs_params(hdaps_idev, ABS_X,
827 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
828 input_set_abs_params(hdaps_idev, ABS_Y,
829 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
831 ret = input_register_device(hdaps_idev);
835 /* initialize the raw data input device */
836 hdaps_idev_raw->name = "ThinkPad HDAPS accelerometer data";
837 hdaps_idev_raw->phys = "hdaps/input1";
838 hdaps_idev_raw->id.bustype = BUS_HOST;
839 hdaps_idev_raw->id.vendor = HDAPS_INPUT_VENDOR;
840 hdaps_idev_raw->id.product = HDAPS_INPUT_PRODUCT;
841 hdaps_idev_raw->id.version = HDAPS_INPUT_RAW_VERSION;
842 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
843 hdaps_idev_raw->cdev.dev = &pdev->dev;
845 hdaps_idev_raw->evbit[0] = BIT(EV_ABS);
846 hdaps_idev_raw->open = hdaps_mousedev_open;
847 hdaps_idev_raw->close = hdaps_mousedev_close;
848 input_set_abs_params(hdaps_idev_raw, ABS_X, -32768, 32767, 0, 0);
849 input_set_abs_params(hdaps_idev_raw, ABS_Y, -32768, 32767, 0, 0);
851 ret = input_register_device(hdaps_idev_raw);
853 goto out_idev_reg_first;
855 printk(KERN_INFO "hdaps: driver successfully loaded.\n");
859 input_unregister_device(hdaps_idev);
861 input_free_device(hdaps_idev_raw);
863 input_free_device(hdaps_idev);
865 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
867 platform_device_unregister(pdev);
869 platform_driver_unregister(&hdaps_driver);
870 hdaps_device_shutdown();
872 printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
876 static void __exit hdaps_exit(void)
878 input_unregister_device(hdaps_idev_raw);
879 input_unregister_device(hdaps_idev);
880 hdaps_device_shutdown(); /* ignore errors, effect is negligible */
881 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
882 platform_device_unregister(pdev);
883 platform_driver_unregister(&hdaps_driver);
885 printk(KERN_INFO "hdaps: driver unloaded.\n");
888 module_init(hdaps_init);
889 module_exit(hdaps_exit);
891 module_param_named(invert, hdaps_invert, uint, 0);
892 MODULE_PARM_DESC(invert, "axis orientation code");
894 MODULE_AUTHOR("Robert Love");
895 MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
896 MODULE_LICENSE("GPL v2");