1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19#include <linux/delay.h>
20#include <linux/platform_device.h>
21#include <linux/input.h>
22#include <linux/kernel.h>
23#include <linux/mutex.h>
24#include <linux/module.h>
25#include <linux/timer.h>
26#include <linux/dmi.h>
27#include <linux/jiffies.h>
28#include <linux/io.h>
29
30#define HDAPS_LOW_PORT 0x1600
31#define HDAPS_NR_PORTS 0x30
32
33#define HDAPS_PORT_STATE 0x1611
34#define HDAPS_PORT_YPOS 0x1612
35#define HDAPS_PORT_XPOS 0x1614
36#define HDAPS_PORT_TEMP1 0x1616
37#define HDAPS_PORT_YVAR 0x1617
38#define HDAPS_PORT_XVAR 0x1619
39#define HDAPS_PORT_TEMP2 0x161b
40#define HDAPS_PORT_UNKNOWN 0x161c
41#define HDAPS_PORT_KMACT 0x161d
42
43#define STATE_FRESH 0x50
44
45#define KEYBD_MASK 0x20
46#define MOUSE_MASK 0x40
47#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK))
48#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK))
49
50#define INIT_TIMEOUT_MSECS 4000
51#define INIT_WAIT_MSECS 200
52
53#define HDAPS_POLL_INTERVAL 50
54#define HDAPS_INPUT_FUZZ 4
55#define HDAPS_INPUT_FLAT 4
56
57#define HDAPS_X_AXIS (1 << 0)
58#define HDAPS_Y_AXIS (1 << 1)
59#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
60
61static struct platform_device *pdev;
62static struct input_dev *hdaps_idev;
63static unsigned int hdaps_invert;
64static u8 km_activity;
65static int rest_x;
66static int rest_y;
67
68static DEFINE_MUTEX(hdaps_mtx);
69
70
71
72
73static inline u8 __get_latch(u16 port)
74{
75 return inb(port) & 0xff;
76}
77
78
79
80
81
82static inline int __check_latch(u16 port, u8 val)
83{
84 if (__get_latch(port) == val)
85 return 0;
86 return -EINVAL;
87}
88
89
90
91
92
93static int __wait_latch(u16 port, u8 val)
94{
95 unsigned int i;
96
97 for (i = 0; i < 20; i++) {
98 if (!__check_latch(port, val))
99 return 0;
100 udelay(5);
101 }
102
103 return -EIO;
104}
105
106
107
108
109
110static void __device_refresh(void)
111{
112 udelay(200);
113 if (inb(0x1604) != STATE_FRESH) {
114 outb(0x11, 0x1610);
115 outb(0x01, 0x161f);
116 }
117}
118
119
120
121
122
123
124static int __device_refresh_sync(void)
125{
126 __device_refresh();
127 return __wait_latch(0x1604, STATE_FRESH);
128}
129
130
131
132
133
134static inline void __device_complete(void)
135{
136 inb(0x161f);
137 inb(0x1604);
138 __device_refresh();
139}
140
141
142
143
144
145
146static int hdaps_readb_one(unsigned int port, u8 *val)
147{
148 int ret;
149
150 mutex_lock(&hdaps_mtx);
151
152
153 ret = __device_refresh_sync();
154 if (ret)
155 goto out;
156
157 *val = inb(port);
158 __device_complete();
159
160out:
161 mutex_unlock(&hdaps_mtx);
162 return ret;
163}
164
165
166static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
167 int *x, int *y)
168{
169
170 if (__device_refresh_sync())
171 return -EIO;
172
173 *y = inw(port2);
174 *x = inw(port1);
175 km_activity = inb(HDAPS_PORT_KMACT);
176 __device_complete();
177
178
179 if (hdaps_invert & HDAPS_X_AXIS)
180 *x = -*x;
181 if (hdaps_invert & HDAPS_Y_AXIS)
182 *y = -*y;
183
184 return 0;
185}
186
187
188
189
190
191static int hdaps_read_pair(unsigned int port1, unsigned int port2,
192 int *val1, int *val2)
193{
194 int ret;
195
196 mutex_lock(&hdaps_mtx);
197 ret = __hdaps_read_pair(port1, port2, val1, val2);
198 mutex_unlock(&hdaps_mtx);
199
200 return ret;
201}
202
203
204
205
206
207static int hdaps_device_init(void)
208{
209 int total, ret = -ENXIO;
210
211 mutex_lock(&hdaps_mtx);
212
213 outb(0x13, 0x1610);
214 outb(0x01, 0x161f);
215 if (__wait_latch(0x161f, 0x00))
216 goto out;
217
218
219
220
221
222
223
224
225
226 if (__check_latch(0x1611, 0x03) &&
227 __check_latch(0x1611, 0x02) &&
228 __check_latch(0x1611, 0x01))
229 goto out;
230
231 printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x)\n",
232 __get_latch(0x1611));
233
234 outb(0x17, 0x1610);
235 outb(0x81, 0x1611);
236 outb(0x01, 0x161f);
237 if (__wait_latch(0x161f, 0x00))
238 goto out;
239 if (__wait_latch(0x1611, 0x00))
240 goto out;
241 if (__wait_latch(0x1612, 0x60))
242 goto out;
243 if (__wait_latch(0x1613, 0x00))
244 goto out;
245 outb(0x14, 0x1610);
246 outb(0x01, 0x1611);
247 outb(0x01, 0x161f);
248 if (__wait_latch(0x161f, 0x00))
249 goto out;
250 outb(0x10, 0x1610);
251 outb(0xc8, 0x1611);
252 outb(0x00, 0x1612);
253 outb(0x02, 0x1613);
254 outb(0x01, 0x161f);
255 if (__wait_latch(0x161f, 0x00))
256 goto out;
257 if (__device_refresh_sync())
258 goto out;
259 if (__wait_latch(0x1611, 0x00))
260 goto out;
261
262
263 for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
264 int x, y;
265
266
267 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
268 if (!__wait_latch(0x1611, 0x02)) {
269 ret = 0;
270 break;
271 }
272
273 msleep(INIT_WAIT_MSECS);
274 }
275
276out:
277 mutex_unlock(&hdaps_mtx);
278 return ret;
279}
280
281
282
283
284static int hdaps_probe(struct platform_device *dev)
285{
286 int ret;
287
288 ret = hdaps_device_init();
289 if (ret)
290 return ret;
291
292 pr_info("device successfully initialized\n");
293 return 0;
294}
295
296#ifdef CONFIG_PM_SLEEP
297static int hdaps_resume(struct device *dev)
298{
299 return hdaps_device_init();
300}
301#endif
302
303static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
304
305static struct platform_driver hdaps_driver = {
306 .probe = hdaps_probe,
307 .driver = {
308 .name = "hdaps",
309 .pm = &hdaps_pm,
310 },
311};
312
313
314
315
316static void hdaps_calibrate(void)
317{
318 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
319}
320
321static void hdaps_mousedev_poll(struct input_dev *input_dev)
322{
323 int x, y;
324
325 mutex_lock(&hdaps_mtx);
326
327 if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
328 goto out;
329
330 input_report_abs(input_dev, ABS_X, x - rest_x);
331 input_report_abs(input_dev, ABS_Y, y - rest_y);
332 input_sync(input_dev);
333
334out:
335 mutex_unlock(&hdaps_mtx);
336}
337
338
339
340
341static ssize_t hdaps_position_show(struct device *dev,
342 struct device_attribute *attr, char *buf)
343{
344 int ret, x, y;
345
346 ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
347 if (ret)
348 return ret;
349
350 return sprintf(buf, "(%d,%d)\n", x, y);
351}
352
353static ssize_t hdaps_variance_show(struct device *dev,
354 struct device_attribute *attr, char *buf)
355{
356 int ret, x, y;
357
358 ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y);
359 if (ret)
360 return ret;
361
362 return sprintf(buf, "(%d,%d)\n", x, y);
363}
364
365static ssize_t hdaps_temp1_show(struct device *dev,
366 struct device_attribute *attr, char *buf)
367{
368 u8 temp;
369 int ret;
370
371 ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
372 if (ret)
373 return ret;
374
375 return sprintf(buf, "%u\n", temp);
376}
377
378static ssize_t hdaps_temp2_show(struct device *dev,
379 struct device_attribute *attr, char *buf)
380{
381 u8 temp;
382 int ret;
383
384 ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
385 if (ret)
386 return ret;
387
388 return sprintf(buf, "%u\n", temp);
389}
390
391static ssize_t hdaps_keyboard_activity_show(struct device *dev,
392 struct device_attribute *attr,
393 char *buf)
394{
395 return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity));
396}
397
398static ssize_t hdaps_mouse_activity_show(struct device *dev,
399 struct device_attribute *attr,
400 char *buf)
401{
402 return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity));
403}
404
405static ssize_t hdaps_calibrate_show(struct device *dev,
406 struct device_attribute *attr, char *buf)
407{
408 return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
409}
410
411static ssize_t hdaps_calibrate_store(struct device *dev,
412 struct device_attribute *attr,
413 const char *buf, size_t count)
414{
415 mutex_lock(&hdaps_mtx);
416 hdaps_calibrate();
417 mutex_unlock(&hdaps_mtx);
418
419 return count;
420}
421
422static ssize_t hdaps_invert_show(struct device *dev,
423 struct device_attribute *attr, char *buf)
424{
425 return sprintf(buf, "%u\n", hdaps_invert);
426}
427
428static ssize_t hdaps_invert_store(struct device *dev,
429 struct device_attribute *attr,
430 const char *buf, size_t count)
431{
432 int invert;
433
434 if (sscanf(buf, "%d", &invert) != 1 ||
435 invert < 0 || invert > HDAPS_BOTH_AXES)
436 return -EINVAL;
437
438 hdaps_invert = invert;
439 hdaps_calibrate();
440
441 return count;
442}
443
444static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
445static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL);
446static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
447static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL);
448static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
449static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
450static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
451static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
452
453static struct attribute *hdaps_attributes[] = {
454 &dev_attr_position.attr,
455 &dev_attr_variance.attr,
456 &dev_attr_temp1.attr,
457 &dev_attr_temp2.attr,
458 &dev_attr_keyboard_activity.attr,
459 &dev_attr_mouse_activity.attr,
460 &dev_attr_calibrate.attr,
461 &dev_attr_invert.attr,
462 NULL,
463};
464
465static struct attribute_group hdaps_attribute_group = {
466 .attrs = hdaps_attributes,
467};
468
469
470
471
472
473static int __init hdaps_dmi_match(const struct dmi_system_id *id)
474{
475 pr_info("%s detected\n", id->ident);
476 return 1;
477}
478
479
480static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
481{
482 hdaps_invert = (unsigned long)id->driver_data;
483 pr_info("inverting axis (%u) readings\n", hdaps_invert);
484 return hdaps_dmi_match(id);
485}
486
487#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
488 .ident = vendor " " model, \
489 .callback = hdaps_dmi_match_invert, \
490 .driver_data = (void *)axes, \
491 .matches = { \
492 DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
493 DMI_MATCH(DMI_PRODUCT_VERSION, model) \
494 } \
495}
496
497#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
498 HDAPS_DMI_MATCH_INVERT(vendor, model, 0)
499
500
501
502
503
504static const struct dmi_system_id hdaps_whitelist[] __initconst = {
505 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
506 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
507 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
508 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
509 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
510 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
511 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
512 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
513 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES),
514 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
515 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
516 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES),
517 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
518 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
519 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
520 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
521 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
522 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
523 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
524 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
525 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
526 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
527 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
528 { .ident = NULL }
529};
530
531static int __init hdaps_init(void)
532{
533 int ret;
534
535 if (!dmi_check_system(hdaps_whitelist)) {
536 pr_warn("supported laptop not found!\n");
537 ret = -ENODEV;
538 goto out;
539 }
540
541 if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
542 ret = -ENXIO;
543 goto out;
544 }
545
546 ret = platform_driver_register(&hdaps_driver);
547 if (ret)
548 goto out_region;
549
550 pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
551 if (IS_ERR(pdev)) {
552 ret = PTR_ERR(pdev);
553 goto out_driver;
554 }
555
556 ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
557 if (ret)
558 goto out_device;
559
560 hdaps_idev = input_allocate_device();
561 if (!hdaps_idev) {
562 ret = -ENOMEM;
563 goto out_group;
564 }
565
566
567 hdaps_calibrate();
568
569
570 hdaps_idev->name = "hdaps";
571 hdaps_idev->phys = "isa1600/input0";
572 hdaps_idev->id.bustype = BUS_ISA;
573 hdaps_idev->dev.parent = &pdev->dev;
574 input_set_abs_params(hdaps_idev, ABS_X,
575 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
576 input_set_abs_params(hdaps_idev, ABS_Y,
577 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
578
579 ret = input_setup_polling(hdaps_idev, hdaps_mousedev_poll);
580 if (ret)
581 goto out_idev;
582
583 input_set_poll_interval(hdaps_idev, HDAPS_POLL_INTERVAL);
584
585 ret = input_register_device(hdaps_idev);
586 if (ret)
587 goto out_idev;
588
589 pr_info("driver successfully loaded\n");
590 return 0;
591
592out_idev:
593 input_free_device(hdaps_idev);
594out_group:
595 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
596out_device:
597 platform_device_unregister(pdev);
598out_driver:
599 platform_driver_unregister(&hdaps_driver);
600out_region:
601 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
602out:
603 pr_warn("driver init failed (ret=%d)!\n", ret);
604 return ret;
605}
606
607static void __exit hdaps_exit(void)
608{
609 input_unregister_device(hdaps_idev);
610 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
611 platform_device_unregister(pdev);
612 platform_driver_unregister(&hdaps_driver);
613 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
614
615 pr_info("driver unloaded\n");
616}
617
618module_init(hdaps_init);
619module_exit(hdaps_exit);
620
621module_param_named(invert, hdaps_invert, int, 0);
622MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, "
623 "2 invert y-axis, 3 invert both axes.");
624
625MODULE_AUTHOR("Robert Love");
626MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
627MODULE_LICENSE("GPL v2");
628