0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0018
0019 #include <linux/delay.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/input.h>
0022 #include <linux/kernel.h>
0023 #include <linux/mutex.h>
0024 #include <linux/module.h>
0025 #include <linux/timer.h>
0026 #include <linux/dmi.h>
0027 #include <linux/jiffies.h>
0028 #include <linux/io.h>
0029
0030 #define HDAPS_LOW_PORT 0x1600
0031 #define HDAPS_NR_PORTS 0x30
0032
0033 #define HDAPS_PORT_STATE 0x1611
0034 #define HDAPS_PORT_YPOS 0x1612
0035 #define HDAPS_PORT_XPOS 0x1614
0036 #define HDAPS_PORT_TEMP1 0x1616
0037 #define HDAPS_PORT_YVAR 0x1617
0038 #define HDAPS_PORT_XVAR 0x1619
0039 #define HDAPS_PORT_TEMP2 0x161b
0040 #define HDAPS_PORT_UNKNOWN 0x161c
0041 #define HDAPS_PORT_KMACT 0x161d
0042
0043 #define STATE_FRESH 0x50
0044
0045 #define KEYBD_MASK 0x20
0046 #define MOUSE_MASK 0x40
0047 #define KEYBD_ISSET(n) (!! (n & KEYBD_MASK))
0048 #define MOUSE_ISSET(n) (!! (n & MOUSE_MASK))
0049
0050 #define INIT_TIMEOUT_MSECS 4000
0051 #define INIT_WAIT_MSECS 200
0052
0053 #define HDAPS_POLL_INTERVAL 50
0054 #define HDAPS_INPUT_FUZZ 4
0055 #define HDAPS_INPUT_FLAT 4
0056
0057 #define HDAPS_X_AXIS (1 << 0)
0058 #define HDAPS_Y_AXIS (1 << 1)
0059 #define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
0060
0061 static struct platform_device *pdev;
0062 static struct input_dev *hdaps_idev;
0063 static unsigned int hdaps_invert;
0064 static u8 km_activity;
0065 static int rest_x;
0066 static int rest_y;
0067
0068 static DEFINE_MUTEX(hdaps_mtx);
0069
0070
0071
0072
0073 static inline u8 __get_latch(u16 port)
0074 {
0075 return inb(port) & 0xff;
0076 }
0077
0078
0079
0080
0081
0082 static inline int __check_latch(u16 port, u8 val)
0083 {
0084 if (__get_latch(port) == val)
0085 return 0;
0086 return -EINVAL;
0087 }
0088
0089
0090
0091
0092
0093 static int __wait_latch(u16 port, u8 val)
0094 {
0095 unsigned int i;
0096
0097 for (i = 0; i < 20; i++) {
0098 if (!__check_latch(port, val))
0099 return 0;
0100 udelay(5);
0101 }
0102
0103 return -EIO;
0104 }
0105
0106
0107
0108
0109
0110 static void __device_refresh(void)
0111 {
0112 udelay(200);
0113 if (inb(0x1604) != STATE_FRESH) {
0114 outb(0x11, 0x1610);
0115 outb(0x01, 0x161f);
0116 }
0117 }
0118
0119
0120
0121
0122
0123
0124 static int __device_refresh_sync(void)
0125 {
0126 __device_refresh();
0127 return __wait_latch(0x1604, STATE_FRESH);
0128 }
0129
0130
0131
0132
0133
0134 static inline void __device_complete(void)
0135 {
0136 inb(0x161f);
0137 inb(0x1604);
0138 __device_refresh();
0139 }
0140
0141
0142
0143
0144
0145
0146 static int hdaps_readb_one(unsigned int port, u8 *val)
0147 {
0148 int ret;
0149
0150 mutex_lock(&hdaps_mtx);
0151
0152
0153 ret = __device_refresh_sync();
0154 if (ret)
0155 goto out;
0156
0157 *val = inb(port);
0158 __device_complete();
0159
0160 out:
0161 mutex_unlock(&hdaps_mtx);
0162 return ret;
0163 }
0164
0165
0166 static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
0167 int *x, int *y)
0168 {
0169
0170 if (__device_refresh_sync())
0171 return -EIO;
0172
0173 *y = inw(port2);
0174 *x = inw(port1);
0175 km_activity = inb(HDAPS_PORT_KMACT);
0176 __device_complete();
0177
0178
0179 if (hdaps_invert & HDAPS_X_AXIS)
0180 *x = -*x;
0181 if (hdaps_invert & HDAPS_Y_AXIS)
0182 *y = -*y;
0183
0184 return 0;
0185 }
0186
0187
0188
0189
0190
0191 static int hdaps_read_pair(unsigned int port1, unsigned int port2,
0192 int *val1, int *val2)
0193 {
0194 int ret;
0195
0196 mutex_lock(&hdaps_mtx);
0197 ret = __hdaps_read_pair(port1, port2, val1, val2);
0198 mutex_unlock(&hdaps_mtx);
0199
0200 return ret;
0201 }
0202
0203
0204
0205
0206
0207 static int hdaps_device_init(void)
0208 {
0209 int total, ret = -ENXIO;
0210
0211 mutex_lock(&hdaps_mtx);
0212
0213 outb(0x13, 0x1610);
0214 outb(0x01, 0x161f);
0215 if (__wait_latch(0x161f, 0x00))
0216 goto out;
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 if (__check_latch(0x1611, 0x03) &&
0227 __check_latch(0x1611, 0x02) &&
0228 __check_latch(0x1611, 0x01))
0229 goto out;
0230
0231 printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x)\n",
0232 __get_latch(0x1611));
0233
0234 outb(0x17, 0x1610);
0235 outb(0x81, 0x1611);
0236 outb(0x01, 0x161f);
0237 if (__wait_latch(0x161f, 0x00))
0238 goto out;
0239 if (__wait_latch(0x1611, 0x00))
0240 goto out;
0241 if (__wait_latch(0x1612, 0x60))
0242 goto out;
0243 if (__wait_latch(0x1613, 0x00))
0244 goto out;
0245 outb(0x14, 0x1610);
0246 outb(0x01, 0x1611);
0247 outb(0x01, 0x161f);
0248 if (__wait_latch(0x161f, 0x00))
0249 goto out;
0250 outb(0x10, 0x1610);
0251 outb(0xc8, 0x1611);
0252 outb(0x00, 0x1612);
0253 outb(0x02, 0x1613);
0254 outb(0x01, 0x161f);
0255 if (__wait_latch(0x161f, 0x00))
0256 goto out;
0257 if (__device_refresh_sync())
0258 goto out;
0259 if (__wait_latch(0x1611, 0x00))
0260 goto out;
0261
0262
0263 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
0264 int x, y;
0265
0266
0267 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
0268 if (!__wait_latch(0x1611, 0x02)) {
0269 ret = 0;
0270 break;
0271 }
0272
0273 msleep(INIT_WAIT_MSECS);
0274 }
0275
0276 out:
0277 mutex_unlock(&hdaps_mtx);
0278 return ret;
0279 }
0280
0281
0282
0283
0284 static int hdaps_probe(struct platform_device *dev)
0285 {
0286 int ret;
0287
0288 ret = hdaps_device_init();
0289 if (ret)
0290 return ret;
0291
0292 pr_info("device successfully initialized\n");
0293 return 0;
0294 }
0295
0296 #ifdef CONFIG_PM_SLEEP
0297 static int hdaps_resume(struct device *dev)
0298 {
0299 return hdaps_device_init();
0300 }
0301 #endif
0302
0303 static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
0304
0305 static struct platform_driver hdaps_driver = {
0306 .probe = hdaps_probe,
0307 .driver = {
0308 .name = "hdaps",
0309 .pm = &hdaps_pm,
0310 },
0311 };
0312
0313
0314
0315
0316 static void hdaps_calibrate(void)
0317 {
0318 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
0319 }
0320
0321 static void hdaps_mousedev_poll(struct input_dev *input_dev)
0322 {
0323 int x, y;
0324
0325 mutex_lock(&hdaps_mtx);
0326
0327 if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
0328 goto out;
0329
0330 input_report_abs(input_dev, ABS_X, x - rest_x);
0331 input_report_abs(input_dev, ABS_Y, y - rest_y);
0332 input_sync(input_dev);
0333
0334 out:
0335 mutex_unlock(&hdaps_mtx);
0336 }
0337
0338
0339
0340
0341 static ssize_t hdaps_position_show(struct device *dev,
0342 struct device_attribute *attr, char *buf)
0343 {
0344 int ret, x, y;
0345
0346 ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
0347 if (ret)
0348 return ret;
0349
0350 return sprintf(buf, "(%d,%d)\n", x, y);
0351 }
0352
0353 static ssize_t hdaps_variance_show(struct device *dev,
0354 struct device_attribute *attr, char *buf)
0355 {
0356 int ret, x, y;
0357
0358 ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y);
0359 if (ret)
0360 return ret;
0361
0362 return sprintf(buf, "(%d,%d)\n", x, y);
0363 }
0364
0365 static ssize_t hdaps_temp1_show(struct device *dev,
0366 struct device_attribute *attr, char *buf)
0367 {
0368 u8 temp;
0369 int ret;
0370
0371 ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
0372 if (ret)
0373 return ret;
0374
0375 return sprintf(buf, "%u\n", temp);
0376 }
0377
0378 static ssize_t hdaps_temp2_show(struct device *dev,
0379 struct device_attribute *attr, char *buf)
0380 {
0381 u8 temp;
0382 int ret;
0383
0384 ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
0385 if (ret)
0386 return ret;
0387
0388 return sprintf(buf, "%u\n", temp);
0389 }
0390
0391 static ssize_t hdaps_keyboard_activity_show(struct device *dev,
0392 struct device_attribute *attr,
0393 char *buf)
0394 {
0395 return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity));
0396 }
0397
0398 static ssize_t hdaps_mouse_activity_show(struct device *dev,
0399 struct device_attribute *attr,
0400 char *buf)
0401 {
0402 return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity));
0403 }
0404
0405 static ssize_t hdaps_calibrate_show(struct device *dev,
0406 struct device_attribute *attr, char *buf)
0407 {
0408 return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
0409 }
0410
0411 static ssize_t hdaps_calibrate_store(struct device *dev,
0412 struct device_attribute *attr,
0413 const char *buf, size_t count)
0414 {
0415 mutex_lock(&hdaps_mtx);
0416 hdaps_calibrate();
0417 mutex_unlock(&hdaps_mtx);
0418
0419 return count;
0420 }
0421
0422 static ssize_t hdaps_invert_show(struct device *dev,
0423 struct device_attribute *attr, char *buf)
0424 {
0425 return sprintf(buf, "%u\n", hdaps_invert);
0426 }
0427
0428 static ssize_t hdaps_invert_store(struct device *dev,
0429 struct device_attribute *attr,
0430 const char *buf, size_t count)
0431 {
0432 int invert;
0433
0434 if (sscanf(buf, "%d", &invert) != 1 ||
0435 invert < 0 || invert > HDAPS_BOTH_AXES)
0436 return -EINVAL;
0437
0438 hdaps_invert = invert;
0439 hdaps_calibrate();
0440
0441 return count;
0442 }
0443
0444 static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
0445 static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL);
0446 static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
0447 static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL);
0448 static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
0449 static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
0450 static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
0451 static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
0452
0453 static struct attribute *hdaps_attributes[] = {
0454 &dev_attr_position.attr,
0455 &dev_attr_variance.attr,
0456 &dev_attr_temp1.attr,
0457 &dev_attr_temp2.attr,
0458 &dev_attr_keyboard_activity.attr,
0459 &dev_attr_mouse_activity.attr,
0460 &dev_attr_calibrate.attr,
0461 &dev_attr_invert.attr,
0462 NULL,
0463 };
0464
0465 static const struct attribute_group hdaps_attribute_group = {
0466 .attrs = hdaps_attributes,
0467 };
0468
0469
0470
0471
0472
0473 static int __init hdaps_dmi_match(const struct dmi_system_id *id)
0474 {
0475 pr_info("%s detected\n", id->ident);
0476 return 1;
0477 }
0478
0479
0480 static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
0481 {
0482 hdaps_invert = (unsigned long)id->driver_data;
0483 pr_info("inverting axis (%u) readings\n", hdaps_invert);
0484 return hdaps_dmi_match(id);
0485 }
0486
0487 #define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
0488 .ident = vendor " " model, \
0489 .callback = hdaps_dmi_match_invert, \
0490 .driver_data = (void *)axes, \
0491 .matches = { \
0492 DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
0493 DMI_MATCH(DMI_PRODUCT_VERSION, model) \
0494 } \
0495 }
0496
0497 #define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
0498 HDAPS_DMI_MATCH_INVERT(vendor, model, 0)
0499
0500
0501
0502
0503
0504 static const struct dmi_system_id hdaps_whitelist[] __initconst = {
0505 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
0506 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
0507 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
0508 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
0509 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
0510 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
0511 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
0512 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
0513 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES),
0514 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
0515 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
0516 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES),
0517 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
0518 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
0519 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
0520 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
0521 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
0522 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
0523 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
0524 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
0525 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
0526 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
0527 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
0528 { .ident = NULL }
0529 };
0530
0531 static int __init hdaps_init(void)
0532 {
0533 int ret;
0534
0535 if (!dmi_check_system(hdaps_whitelist)) {
0536 pr_warn("supported laptop not found!\n");
0537 ret = -ENODEV;
0538 goto out;
0539 }
0540
0541 if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
0542 ret = -ENXIO;
0543 goto out;
0544 }
0545
0546 ret = platform_driver_register(&hdaps_driver);
0547 if (ret)
0548 goto out_region;
0549
0550 pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
0551 if (IS_ERR(pdev)) {
0552 ret = PTR_ERR(pdev);
0553 goto out_driver;
0554 }
0555
0556 ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
0557 if (ret)
0558 goto out_device;
0559
0560 hdaps_idev = input_allocate_device();
0561 if (!hdaps_idev) {
0562 ret = -ENOMEM;
0563 goto out_group;
0564 }
0565
0566
0567 hdaps_calibrate();
0568
0569
0570 hdaps_idev->name = "hdaps";
0571 hdaps_idev->phys = "isa1600/input0";
0572 hdaps_idev->id.bustype = BUS_ISA;
0573 hdaps_idev->dev.parent = &pdev->dev;
0574 input_set_abs_params(hdaps_idev, ABS_X,
0575 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
0576 input_set_abs_params(hdaps_idev, ABS_Y,
0577 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
0578
0579 ret = input_setup_polling(hdaps_idev, hdaps_mousedev_poll);
0580 if (ret)
0581 goto out_idev;
0582
0583 input_set_poll_interval(hdaps_idev, HDAPS_POLL_INTERVAL);
0584
0585 ret = input_register_device(hdaps_idev);
0586 if (ret)
0587 goto out_idev;
0588
0589 pr_info("driver successfully loaded\n");
0590 return 0;
0591
0592 out_idev:
0593 input_free_device(hdaps_idev);
0594 out_group:
0595 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
0596 out_device:
0597 platform_device_unregister(pdev);
0598 out_driver:
0599 platform_driver_unregister(&hdaps_driver);
0600 out_region:
0601 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
0602 out:
0603 pr_warn("driver init failed (ret=%d)!\n", ret);
0604 return ret;
0605 }
0606
0607 static void __exit hdaps_exit(void)
0608 {
0609 input_unregister_device(hdaps_idev);
0610 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
0611 platform_device_unregister(pdev);
0612 platform_driver_unregister(&hdaps_driver);
0613 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
0614
0615 pr_info("driver unloaded\n");
0616 }
0617
0618 module_init(hdaps_init);
0619 module_exit(hdaps_exit);
0620
0621 module_param_named(invert, hdaps_invert, int, 0);
0622 MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, "
0623 "2 invert y-axis, 3 invert both axes.");
0624
0625 MODULE_AUTHOR("Robert Love");
0626 MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
0627 MODULE_LICENSE("GPL v2");