This source file includes following definitions.
- __get_latch
- __check_latch
- __wait_latch
- __device_refresh
- __device_refresh_sync
- __device_complete
- hdaps_readb_one
- __hdaps_read_pair
- hdaps_read_pair
- hdaps_device_init
- hdaps_probe
- hdaps_resume
- hdaps_calibrate
- hdaps_mousedev_poll
- hdaps_position_show
- hdaps_variance_show
- hdaps_temp1_show
- hdaps_temp2_show
- hdaps_keyboard_activity_show
- hdaps_mouse_activity_show
- hdaps_calibrate_show
- hdaps_calibrate_store
- hdaps_invert_show
- hdaps_invert_store
- hdaps_dmi_match
- hdaps_dmi_match_invert
- hdaps_init
- hdaps_exit
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-polldev.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
61 static struct platform_device *pdev;
62 static struct input_polled_dev *hdaps_idev;
63 static unsigned int hdaps_invert;
64 static u8 km_activity;
65 static int rest_x;
66 static int rest_y;
67
68 static DEFINE_MUTEX(hdaps_mtx);
69
70
71
72
73 static inline u8 __get_latch(u16 port)
74 {
75 return inb(port) & 0xff;
76 }
77
78
79
80
81
82 static 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
93 static 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
110 static 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
124 static int __device_refresh_sync(void)
125 {
126 __device_refresh();
127 return __wait_latch(0x1604, STATE_FRESH);
128 }
129
130
131
132
133
134 static inline void __device_complete(void)
135 {
136 inb(0x161f);
137 inb(0x1604);
138 __device_refresh();
139 }
140
141
142
143
144
145
146 static 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
160 out:
161 mutex_unlock(&hdaps_mtx);
162 return ret;
163 }
164
165
166 static 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
191 static 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
207 static 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
276 out:
277 mutex_unlock(&hdaps_mtx);
278 return ret;
279 }
280
281
282
283
284 static 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
297 static int hdaps_resume(struct device *dev)
298 {
299 return hdaps_device_init();
300 }
301 #endif
302
303 static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
304
305 static struct platform_driver hdaps_driver = {
306 .probe = hdaps_probe,
307 .driver = {
308 .name = "hdaps",
309 .pm = &hdaps_pm,
310 },
311 };
312
313
314
315
316 static void hdaps_calibrate(void)
317 {
318 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
319 }
320
321 static void hdaps_mousedev_poll(struct input_polled_dev *dev)
322 {
323 struct input_dev *input_dev = dev->input;
324 int x, y;
325
326 mutex_lock(&hdaps_mtx);
327
328 if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
329 goto out;
330
331 input_report_abs(input_dev, ABS_X, x - rest_x);
332 input_report_abs(input_dev, ABS_Y, y - rest_y);
333 input_sync(input_dev);
334
335 out:
336 mutex_unlock(&hdaps_mtx);
337 }
338
339
340
341
342 static ssize_t hdaps_position_show(struct device *dev,
343 struct device_attribute *attr, char *buf)
344 {
345 int ret, x, y;
346
347 ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
348 if (ret)
349 return ret;
350
351 return sprintf(buf, "(%d,%d)\n", x, y);
352 }
353
354 static ssize_t hdaps_variance_show(struct device *dev,
355 struct device_attribute *attr, char *buf)
356 {
357 int ret, x, y;
358
359 ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y);
360 if (ret)
361 return ret;
362
363 return sprintf(buf, "(%d,%d)\n", x, y);
364 }
365
366 static ssize_t hdaps_temp1_show(struct device *dev,
367 struct device_attribute *attr, char *buf)
368 {
369 u8 uninitialized_var(temp);
370 int ret;
371
372 ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
373 if (ret)
374 return ret;
375
376 return sprintf(buf, "%u\n", temp);
377 }
378
379 static ssize_t hdaps_temp2_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_TEMP2, &temp);
386 if (ret)
387 return ret;
388
389 return sprintf(buf, "%u\n", temp);
390 }
391
392 static ssize_t hdaps_keyboard_activity_show(struct device *dev,
393 struct device_attribute *attr,
394 char *buf)
395 {
396 return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity));
397 }
398
399 static ssize_t hdaps_mouse_activity_show(struct device *dev,
400 struct device_attribute *attr,
401 char *buf)
402 {
403 return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity));
404 }
405
406 static ssize_t hdaps_calibrate_show(struct device *dev,
407 struct device_attribute *attr, char *buf)
408 {
409 return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
410 }
411
412 static ssize_t hdaps_calibrate_store(struct device *dev,
413 struct device_attribute *attr,
414 const char *buf, size_t count)
415 {
416 mutex_lock(&hdaps_mtx);
417 hdaps_calibrate();
418 mutex_unlock(&hdaps_mtx);
419
420 return count;
421 }
422
423 static ssize_t hdaps_invert_show(struct device *dev,
424 struct device_attribute *attr, char *buf)
425 {
426 return sprintf(buf, "%u\n", hdaps_invert);
427 }
428
429 static ssize_t hdaps_invert_store(struct device *dev,
430 struct device_attribute *attr,
431 const char *buf, size_t count)
432 {
433 int invert;
434
435 if (sscanf(buf, "%d", &invert) != 1 ||
436 invert < 0 || invert > HDAPS_BOTH_AXES)
437 return -EINVAL;
438
439 hdaps_invert = invert;
440 hdaps_calibrate();
441
442 return count;
443 }
444
445 static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
446 static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL);
447 static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
448 static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL);
449 static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
450 static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
451 static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
452 static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
453
454 static struct attribute *hdaps_attributes[] = {
455 &dev_attr_position.attr,
456 &dev_attr_variance.attr,
457 &dev_attr_temp1.attr,
458 &dev_attr_temp2.attr,
459 &dev_attr_keyboard_activity.attr,
460 &dev_attr_mouse_activity.attr,
461 &dev_attr_calibrate.attr,
462 &dev_attr_invert.attr,
463 NULL,
464 };
465
466 static struct attribute_group hdaps_attribute_group = {
467 .attrs = hdaps_attributes,
468 };
469
470
471
472
473
474 static int __init hdaps_dmi_match(const struct dmi_system_id *id)
475 {
476 pr_info("%s detected\n", id->ident);
477 return 1;
478 }
479
480
481 static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
482 {
483 hdaps_invert = (unsigned long)id->driver_data;
484 pr_info("inverting axis (%u) readings\n", hdaps_invert);
485 return hdaps_dmi_match(id);
486 }
487
488 #define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
489 .ident = vendor " " model, \
490 .callback = hdaps_dmi_match_invert, \
491 .driver_data = (void *)axes, \
492 .matches = { \
493 DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
494 DMI_MATCH(DMI_PRODUCT_VERSION, model) \
495 } \
496 }
497
498 #define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
499 HDAPS_DMI_MATCH_INVERT(vendor, model, 0)
500
501
502
503
504
505 static const struct dmi_system_id hdaps_whitelist[] __initconst = {
506 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
507 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
508 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
509 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
510 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
511 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
512 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
513 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
514 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES),
515 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
516 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
517 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES),
518 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
519 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
520 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
521 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
522 HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
523 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
524 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
525 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
526 HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
527 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
528 HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
529 { .ident = NULL }
530 };
531
532 static int __init hdaps_init(void)
533 {
534 struct input_dev *idev;
535 int ret;
536
537 if (!dmi_check_system(hdaps_whitelist)) {
538 pr_warn("supported laptop not found!\n");
539 ret = -ENODEV;
540 goto out;
541 }
542
543 if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
544 ret = -ENXIO;
545 goto out;
546 }
547
548 ret = platform_driver_register(&hdaps_driver);
549 if (ret)
550 goto out_region;
551
552 pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
553 if (IS_ERR(pdev)) {
554 ret = PTR_ERR(pdev);
555 goto out_driver;
556 }
557
558 ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
559 if (ret)
560 goto out_device;
561
562 hdaps_idev = input_allocate_polled_device();
563 if (!hdaps_idev) {
564 ret = -ENOMEM;
565 goto out_group;
566 }
567
568 hdaps_idev->poll = hdaps_mousedev_poll;
569 hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
570
571
572 hdaps_calibrate();
573
574
575 idev = hdaps_idev->input;
576 idev->name = "hdaps";
577 idev->phys = "isa1600/input0";
578 idev->id.bustype = BUS_ISA;
579 idev->dev.parent = &pdev->dev;
580 idev->evbit[0] = BIT_MASK(EV_ABS);
581 input_set_abs_params(idev, ABS_X,
582 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
583 input_set_abs_params(idev, ABS_Y,
584 -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
585
586 ret = input_register_polled_device(hdaps_idev);
587 if (ret)
588 goto out_idev;
589
590 pr_info("driver successfully loaded\n");
591 return 0;
592
593 out_idev:
594 input_free_polled_device(hdaps_idev);
595 out_group:
596 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
597 out_device:
598 platform_device_unregister(pdev);
599 out_driver:
600 platform_driver_unregister(&hdaps_driver);
601 out_region:
602 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
603 out:
604 pr_warn("driver init failed (ret=%d)!\n", ret);
605 return ret;
606 }
607
608 static void __exit hdaps_exit(void)
609 {
610 input_unregister_polled_device(hdaps_idev);
611 input_free_polled_device(hdaps_idev);
612 sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
613 platform_device_unregister(pdev);
614 platform_driver_unregister(&hdaps_driver);
615 release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
616
617 pr_info("driver unloaded\n");
618 }
619
620 module_init(hdaps_init);
621 module_exit(hdaps_exit);
622
623 module_param_named(invert, hdaps_invert, int, 0);
624 MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, "
625 "2 invert y-axis, 3 invert both axes.");
626
627 MODULE_AUTHOR("Robert Love");
628 MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
629 MODULE_LICENSE("GPL v2");