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