diff options
Diffstat (limited to 'drivers/hwmon/hdaps.c')
| -rw-r--r-- | drivers/hwmon/hdaps.c | 369 |
1 files changed, 123 insertions, 246 deletions
diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index eaebfc14c933..7f0107613827 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c | |||
| @@ -4,11 +4,11 @@ | |||
| 4 | * Copyright (C) 2005 Robert Love <rml@novell.com> | 4 | * Copyright (C) 2005 Robert Love <rml@novell.com> |
| 5 | * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> | 5 | * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> |
| 6 | * | 6 | * |
| 7 | * The HardDisk Active Protection System (hdaps) is present in the IBM ThinkPad | 7 | * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads |
| 8 | * T41, T42, T43, R51, and X40, at least. It provides a basic two-axis | 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. | 9 | * accelerometer and other data, such as the device's temperature. |
| 10 | * | 10 | * |
| 11 | * Based on the document by Mark A. Smith available at | 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 | 12 | * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial |
| 13 | * and error. | 13 | * and error. |
| 14 | * | 14 | * |
| @@ -36,12 +36,7 @@ | |||
| 36 | #include <asm/io.h> | 36 | #include <asm/io.h> |
| 37 | 37 | ||
| 38 | #define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ | 38 | #define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ |
| 39 | #define HDAPS_NR_PORTS 0x30 /* 0x1600 - 0x162f */ | 39 | #define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ |
| 40 | |||
| 41 | #define STATE_FRESH 0x50 /* accelerometer data is fresh */ | ||
| 42 | |||
| 43 | #define REFRESH_ASYNC 0x00 /* do asynchronous refresh */ | ||
| 44 | #define REFRESH_SYNC 0x01 /* do synchronous refresh */ | ||
| 45 | 40 | ||
| 46 | #define HDAPS_PORT_STATE 0x1611 /* device state */ | 41 | #define HDAPS_PORT_STATE 0x1611 /* device state */ |
| 47 | #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ | 42 | #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ |
| @@ -53,7 +48,7 @@ | |||
| 53 | #define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ | 48 | #define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ |
| 54 | #define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ | 49 | #define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ |
| 55 | 50 | ||
| 56 | #define HDAPS_READ_MASK 0xff /* some reads have the low 8 bits set */ | 51 | #define STATE_FRESH 0x50 /* accelerometer data is fresh */ |
| 57 | 52 | ||
| 58 | #define KEYBD_MASK 0x20 /* set if keyboard activity */ | 53 | #define KEYBD_MASK 0x20 /* set if keyboard activity */ |
| 59 | #define MOUSE_MASK 0x40 /* set if mouse activity */ | 54 | #define MOUSE_MASK 0x40 /* set if mouse activity */ |
| @@ -63,12 +58,11 @@ | |||
| 63 | #define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ | 58 | #define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ |
| 64 | #define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ | 59 | #define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ |
| 65 | 60 | ||
| 66 | static struct platform_device *pdev; | 61 | #define HDAPS_POLL_PERIOD (HZ/20) /* poll for input every 1/20s */ |
| 67 | static struct input_dev hdaps_idev; | 62 | #define HDAPS_INPUT_FUZZ 4 /* input event threshold */ |
| 63 | |||
| 68 | static struct timer_list hdaps_timer; | 64 | static struct timer_list hdaps_timer; |
| 69 | static unsigned int hdaps_mousedev_threshold = 4; | 65 | static struct platform_device *pdev; |
| 70 | static unsigned long hdaps_poll_ms = 50; | ||
| 71 | static unsigned int hdaps_mousedev; | ||
| 72 | static unsigned int hdaps_invert; | 66 | static unsigned int hdaps_invert; |
| 73 | static u8 km_activity; | 67 | static u8 km_activity; |
| 74 | static int rest_x; | 68 | static int rest_x; |
| @@ -81,14 +75,14 @@ static DECLARE_MUTEX(hdaps_sem); | |||
| 81 | */ | 75 | */ |
| 82 | static inline u8 __get_latch(u16 port) | 76 | static inline u8 __get_latch(u16 port) |
| 83 | { | 77 | { |
| 84 | return inb(port) & HDAPS_READ_MASK; | 78 | return inb(port) & 0xff; |
| 85 | } | 79 | } |
| 86 | 80 | ||
| 87 | /* | 81 | /* |
| 88 | * __check_latch - Check a port latch for a given value. Callers must hold | 82 | * __check_latch - Check a port latch for a given value. Returns zero if the |
| 89 | * hdaps_sem. Returns zero if the port contains the given value. | 83 | * port contains the given value. Callers must hold hdaps_sem. |
| 90 | */ | 84 | */ |
| 91 | static inline unsigned int __check_latch(u16 port, u8 val) | 85 | static inline int __check_latch(u16 port, u8 val) |
| 92 | { | 86 | { |
| 93 | if (__get_latch(port) == val) | 87 | if (__get_latch(port) == val) |
| 94 | return 0; | 88 | return 0; |
| @@ -99,7 +93,7 @@ static inline unsigned int __check_latch(u16 port, u8 val) | |||
| 99 | * __wait_latch - Wait up to 100us for a port latch to get a certain value, | 93 | * __wait_latch - Wait up to 100us for a port latch to get a certain value, |
| 100 | * returning zero if the value is obtained. Callers must hold hdaps_sem. | 94 | * returning zero if the value is obtained. Callers must hold hdaps_sem. |
| 101 | */ | 95 | */ |
| 102 | static unsigned int __wait_latch(u16 port, u8 val) | 96 | static int __wait_latch(u16 port, u8 val) |
| 103 | { | 97 | { |
| 104 | unsigned int i; | 98 | unsigned int i; |
| 105 | 99 | ||
| @@ -109,59 +103,42 @@ static unsigned int __wait_latch(u16 port, u8 val) | |||
| 109 | udelay(5); | 103 | udelay(5); |
| 110 | } | 104 | } |
| 111 | 105 | ||
| 112 | return -EINVAL; | 106 | return -EIO; |
| 113 | } | 107 | } |
| 114 | 108 | ||
| 115 | /* | 109 | /* |
| 116 | * __device_refresh - Request a refresh from the accelerometer. | 110 | * __device_refresh - request a refresh from the accelerometer. Does not wait |
| 117 | * | 111 | * for refresh to complete. Callers must hold hdaps_sem. |
| 118 | * If sync is REFRESH_SYNC, we perform a synchronous refresh and will wait. | ||
| 119 | * Returns zero if successful and nonzero on error. | ||
| 120 | * | ||
| 121 | * If sync is REFRESH_ASYNC, we merely kick off a new refresh if the device is | ||
| 122 | * not up-to-date. Always returns zero. | ||
| 123 | * | ||
| 124 | * Callers must hold hdaps_sem. | ||
| 125 | */ | 112 | */ |
| 126 | static int __device_refresh(unsigned int sync) | 113 | static void __device_refresh(void) |
| 127 | { | 114 | { |
| 128 | u8 state; | 115 | udelay(200); |
| 129 | 116 | if (inb(0x1604) != STATE_FRESH) { | |
| 130 | udelay(100); | 117 | outb(0x11, 0x1610); |
| 131 | 118 | outb(0x01, 0x161f); | |
| 132 | state = inb(0x1604); | 119 | } |
| 133 | if (state == STATE_FRESH) | 120 | } |
| 134 | return 0; | ||
| 135 | |||
| 136 | outb(0x11, 0x1610); | ||
| 137 | outb(0x01, 0x161f); | ||
| 138 | if (sync == REFRESH_ASYNC) | ||
| 139 | return 0; | ||
| 140 | 121 | ||
| 122 | /* | ||
| 123 | * __device_refresh_sync - request a synchronous refresh from the | ||
| 124 | * accelerometer. We wait for the refresh to complete. Returns zero if | ||
| 125 | * successful and nonzero on error. Callers must hold hdaps_sem. | ||
| 126 | */ | ||
| 127 | static int __device_refresh_sync(void) | ||
| 128 | { | ||
| 129 | __device_refresh(); | ||
| 141 | return __wait_latch(0x1604, STATE_FRESH); | 130 | return __wait_latch(0x1604, STATE_FRESH); |
| 142 | } | 131 | } |
| 143 | 132 | ||
| 144 | /* | 133 | /* |
| 145 | * __device_complete - Indicate to the accelerometer that we are done reading | 134 | * __device_complete - indicate to the accelerometer that we are done reading |
| 146 | * data, and then initiate an async refresh. Callers must hold hdaps_sem. | 135 | * data, and then initiate an async refresh. Callers must hold hdaps_sem. |
| 147 | */ | 136 | */ |
| 148 | static inline void __device_complete(void) | 137 | static inline void __device_complete(void) |
| 149 | { | 138 | { |
| 150 | inb(0x161f); | 139 | inb(0x161f); |
| 151 | inb(0x1604); | 140 | inb(0x1604); |
| 152 | __device_refresh(REFRESH_ASYNC); | 141 | __device_refresh(); |
| 153 | } | ||
| 154 | |||
| 155 | static int __hdaps_readb_one(unsigned int port, u8 *val) | ||
| 156 | { | ||
| 157 | /* do a sync refresh -- we need to be sure that we read fresh data */ | ||
| 158 | if (__device_refresh(REFRESH_SYNC)) | ||
| 159 | return -EIO; | ||
| 160 | |||
| 161 | *val = inb(port); | ||
| 162 | __device_complete(); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | } | 142 | } |
| 166 | 143 | ||
| 167 | /* | 144 | /* |
| @@ -174,17 +151,26 @@ static int hdaps_readb_one(unsigned int port, u8 *val) | |||
| 174 | int ret; | 151 | int ret; |
| 175 | 152 | ||
| 176 | down(&hdaps_sem); | 153 | down(&hdaps_sem); |
| 177 | ret = __hdaps_readb_one(port, val); | ||
| 178 | up(&hdaps_sem); | ||
| 179 | 154 | ||
| 155 | /* do a sync refresh -- we need to be sure that we read fresh data */ | ||
| 156 | ret = __device_refresh_sync(); | ||
| 157 | if (ret) | ||
| 158 | goto out; | ||
| 159 | |||
| 160 | *val = inb(port); | ||
| 161 | __device_complete(); | ||
| 162 | |||
| 163 | out: | ||
| 164 | up(&hdaps_sem); | ||
| 180 | return ret; | 165 | return ret; |
| 181 | } | 166 | } |
| 182 | 167 | ||
| 168 | /* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ | ||
| 183 | static int __hdaps_read_pair(unsigned int port1, unsigned int port2, | 169 | static int __hdaps_read_pair(unsigned int port1, unsigned int port2, |
| 184 | int *x, int *y) | 170 | int *x, int *y) |
| 185 | { | 171 | { |
| 186 | /* do a sync refresh -- we need to be sure that we read fresh data */ | 172 | /* do a sync refresh -- we need to be sure that we read fresh data */ |
| 187 | if (__device_refresh(REFRESH_SYNC)) | 173 | if (__device_refresh_sync()) |
| 188 | return -EIO; | 174 | return -EIO; |
| 189 | 175 | ||
| 190 | *y = inw(port2); | 176 | *y = inw(port2); |
| @@ -217,11 +203,13 @@ static int hdaps_read_pair(unsigned int port1, unsigned int port2, | |||
| 217 | return ret; | 203 | return ret; |
| 218 | } | 204 | } |
| 219 | 205 | ||
| 220 | /* initialize the accelerometer */ | 206 | /* |
| 207 | * hdaps_device_init - initialize the accelerometer. Returns zero on success | ||
| 208 | * and negative error code on failure. Can sleep. | ||
| 209 | */ | ||
| 221 | static int hdaps_device_init(void) | 210 | static int hdaps_device_init(void) |
| 222 | { | 211 | { |
| 223 | unsigned int total_msecs = INIT_TIMEOUT_MSECS; | 212 | int total, ret = -ENXIO; |
| 224 | int ret = -ENXIO; | ||
| 225 | 213 | ||
| 226 | down(&hdaps_sem); | 214 | down(&hdaps_sem); |
| 227 | 215 | ||
| @@ -231,8 +219,10 @@ static int hdaps_device_init(void) | |||
| 231 | goto out; | 219 | goto out; |
| 232 | 220 | ||
| 233 | /* | 221 | /* |
| 234 | * The 0x03 value appears to only work on some thinkpads, such as the | 222 | * Most ThinkPads return 0x01. |
| 235 | * T42p. Others return 0x01. | 223 | * |
| 224 | * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops | ||
| 225 | * have "inverted" axises. | ||
| 236 | * | 226 | * |
| 237 | * The 0x02 value occurs when the chip has been previously initialized. | 227 | * The 0x02 value occurs when the chip has been previously initialized. |
| 238 | */ | 228 | */ |
| @@ -267,24 +257,23 @@ static int hdaps_device_init(void) | |||
| 267 | outb(0x01, 0x161f); | 257 | outb(0x01, 0x161f); |
| 268 | if (__wait_latch(0x161f, 0x00)) | 258 | if (__wait_latch(0x161f, 0x00)) |
| 269 | goto out; | 259 | goto out; |
| 270 | if (__device_refresh(REFRESH_SYNC)) | 260 | if (__device_refresh_sync()) |
| 271 | goto out; | 261 | goto out; |
| 272 | if (__wait_latch(0x1611, 0x00)) | 262 | if (__wait_latch(0x1611, 0x00)) |
| 273 | goto out; | 263 | goto out; |
| 274 | 264 | ||
| 275 | /* we have done our dance, now let's wait for the applause */ | 265 | /* we have done our dance, now let's wait for the applause */ |
| 276 | while (total_msecs > 0) { | 266 | for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { |
| 277 | u8 ignored; | 267 | int x, y; |
| 278 | 268 | ||
| 279 | /* a read of the device helps push it into action */ | 269 | /* a read of the device helps push it into action */ |
| 280 | __hdaps_readb_one(HDAPS_PORT_UNKNOWN, &ignored); | 270 | __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); |
| 281 | if (!__wait_latch(0x1611, 0x02)) { | 271 | if (!__wait_latch(0x1611, 0x02)) { |
| 282 | ret = 0; | 272 | ret = 0; |
| 283 | break; | 273 | break; |
| 284 | } | 274 | } |
| 285 | 275 | ||
| 286 | msleep(INIT_WAIT_MSECS); | 276 | msleep(INIT_WAIT_MSECS); |
| 287 | total_msecs -= INIT_WAIT_MSECS; | ||
| 288 | } | 277 | } |
| 289 | 278 | ||
| 290 | out: | 279 | out: |
| @@ -293,96 +282,6 @@ out: | |||
| 293 | } | 282 | } |
| 294 | 283 | ||
| 295 | 284 | ||
| 296 | /* Input class stuff */ | ||
| 297 | |||
| 298 | /* | ||
| 299 | * hdaps_calibrate - Zero out our "resting" values. Callers must hold hdaps_sem. | ||
| 300 | */ | ||
| 301 | static void hdaps_calibrate(void) | ||
| 302 | { | ||
| 303 | int x, y; | ||
| 304 | |||
| 305 | if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) | ||
| 306 | return; | ||
| 307 | |||
| 308 | rest_x = x; | ||
| 309 | rest_y = y; | ||
| 310 | } | ||
| 311 | |||
| 312 | static void hdaps_mousedev_poll(unsigned long unused) | ||
| 313 | { | ||
| 314 | int x, y; | ||
| 315 | |||
| 316 | /* Cannot sleep. Try nonblockingly. If we fail, try again later. */ | ||
| 317 | if (down_trylock(&hdaps_sem)) { | ||
| 318 | mod_timer(&hdaps_timer,jiffies+msecs_to_jiffies(hdaps_poll_ms)); | ||
| 319 | return; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) | ||
| 323 | goto out; | ||
| 324 | |||
| 325 | x -= rest_x; | ||
| 326 | y -= rest_y; | ||
| 327 | if (abs(x) > hdaps_mousedev_threshold) | ||
| 328 | input_report_rel(&hdaps_idev, REL_X, x); | ||
| 329 | if (abs(y) > hdaps_mousedev_threshold) | ||
| 330 | input_report_rel(&hdaps_idev, REL_Y, y); | ||
| 331 | input_sync(&hdaps_idev); | ||
| 332 | |||
| 333 | mod_timer(&hdaps_timer, jiffies + msecs_to_jiffies(hdaps_poll_ms)); | ||
| 334 | |||
| 335 | out: | ||
| 336 | up(&hdaps_sem); | ||
| 337 | } | ||
| 338 | |||
| 339 | /* | ||
| 340 | * hdaps_mousedev_enable - enable the input class device. Can sleep. | ||
| 341 | */ | ||
| 342 | static void hdaps_mousedev_enable(void) | ||
| 343 | { | ||
| 344 | down(&hdaps_sem); | ||
| 345 | |||
| 346 | /* calibrate the device before enabling */ | ||
| 347 | hdaps_calibrate(); | ||
| 348 | |||
| 349 | /* initialize the input class */ | ||
| 350 | init_input_dev(&hdaps_idev); | ||
| 351 | hdaps_idev.dev = &pdev->dev; | ||
| 352 | hdaps_idev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
| 353 | hdaps_idev.relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
| 354 | hdaps_idev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT); | ||
| 355 | input_register_device(&hdaps_idev); | ||
| 356 | |||
| 357 | /* start up our timer */ | ||
| 358 | init_timer(&hdaps_timer); | ||
| 359 | hdaps_timer.function = hdaps_mousedev_poll; | ||
| 360 | hdaps_timer.expires = jiffies + msecs_to_jiffies(hdaps_poll_ms); | ||
| 361 | add_timer(&hdaps_timer); | ||
| 362 | |||
| 363 | hdaps_mousedev = 1; | ||
| 364 | |||
| 365 | up(&hdaps_sem); | ||
| 366 | |||
| 367 | printk(KERN_INFO "hdaps: input device enabled.\n"); | ||
| 368 | } | ||
| 369 | |||
| 370 | /* | ||
| 371 | * hdaps_mousedev_disable - disable the input class device. Caller must hold | ||
| 372 | * hdaps_sem. | ||
| 373 | */ | ||
| 374 | static void hdaps_mousedev_disable(void) | ||
| 375 | { | ||
| 376 | down(&hdaps_sem); | ||
| 377 | if (hdaps_mousedev) { | ||
| 378 | hdaps_mousedev = 0; | ||
| 379 | del_timer_sync(&hdaps_timer); | ||
| 380 | input_unregister_device(&hdaps_idev); | ||
| 381 | } | ||
| 382 | up(&hdaps_sem); | ||
| 383 | } | ||
| 384 | |||
| 385 | |||
| 386 | /* Device model stuff */ | 285 | /* Device model stuff */ |
| 387 | 286 | ||
| 388 | static int hdaps_probe(struct device *dev) | 287 | static int hdaps_probe(struct device *dev) |
| @@ -412,6 +311,49 @@ static struct device_driver hdaps_driver = { | |||
| 412 | .resume = hdaps_resume | 311 | .resume = hdaps_resume |
| 413 | }; | 312 | }; |
| 414 | 313 | ||
| 314 | /* Input class stuff */ | ||
| 315 | |||
| 316 | static struct input_dev hdaps_idev = { | ||
| 317 | .name = "hdaps", | ||
| 318 | .evbit = { BIT(EV_ABS) }, | ||
| 319 | .absbit = { BIT(ABS_X) | BIT(ABS_Y) }, | ||
| 320 | .absmin = { [ABS_X] = -256, [ABS_Y] = -256 }, | ||
| 321 | .absmax = { [ABS_X] = 256, [ABS_Y] = 256 }, | ||
| 322 | .absfuzz = { [ABS_X] = HDAPS_INPUT_FUZZ, [ABS_Y] = HDAPS_INPUT_FUZZ }, | ||
| 323 | .absflat = { [ABS_X] = HDAPS_INPUT_FUZZ, [ABS_Y] = HDAPS_INPUT_FUZZ }, | ||
| 324 | }; | ||
| 325 | |||
| 326 | /* | ||
| 327 | * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_sem. | ||
| 328 | */ | ||
| 329 | static void hdaps_calibrate(void) | ||
| 330 | { | ||
| 331 | __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); | ||
| 332 | } | ||
| 333 | |||
| 334 | static void hdaps_mousedev_poll(unsigned long unused) | ||
| 335 | { | ||
| 336 | int x, y; | ||
| 337 | |||
| 338 | /* Cannot sleep. Try nonblockingly. If we fail, try again later. */ | ||
| 339 | if (down_trylock(&hdaps_sem)) { | ||
| 340 | mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD); | ||
| 341 | return; | ||
| 342 | } | ||
| 343 | |||
| 344 | if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) | ||
| 345 | goto out; | ||
| 346 | |||
| 347 | input_report_abs(&hdaps_idev, ABS_X, x - rest_x); | ||
| 348 | input_report_abs(&hdaps_idev, ABS_Y, y - rest_y); | ||
| 349 | input_sync(&hdaps_idev); | ||
| 350 | |||
| 351 | mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD); | ||
| 352 | |||
| 353 | out: | ||
| 354 | up(&hdaps_sem); | ||
| 355 | } | ||
| 356 | |||
| 415 | 357 | ||
| 416 | /* Sysfs Files */ | 358 | /* Sysfs Files */ |
| 417 | 359 | ||
| @@ -517,69 +459,6 @@ static ssize_t hdaps_invert_store(struct device *dev, | |||
| 517 | return count; | 459 | return count; |
| 518 | } | 460 | } |
| 519 | 461 | ||
| 520 | static ssize_t hdaps_mousedev_show(struct device *dev, | ||
| 521 | struct device_attribute *attr, char *buf) | ||
| 522 | { | ||
| 523 | return sprintf(buf, "%d\n", hdaps_mousedev); | ||
| 524 | } | ||
| 525 | |||
| 526 | static ssize_t hdaps_mousedev_store(struct device *dev, | ||
| 527 | struct device_attribute *attr, | ||
| 528 | const char *buf, size_t count) | ||
| 529 | { | ||
| 530 | int enable; | ||
| 531 | |||
| 532 | if (sscanf(buf, "%d", &enable) != 1) | ||
| 533 | return -EINVAL; | ||
| 534 | |||
| 535 | if (enable == 1) | ||
| 536 | hdaps_mousedev_enable(); | ||
| 537 | else if (enable == 0) | ||
| 538 | hdaps_mousedev_disable(); | ||
| 539 | else | ||
| 540 | return -EINVAL; | ||
| 541 | |||
| 542 | return count; | ||
| 543 | } | ||
| 544 | |||
| 545 | static ssize_t hdaps_poll_show(struct device *dev, | ||
| 546 | struct device_attribute *attr, char *buf) | ||
| 547 | { | ||
| 548 | return sprintf(buf, "%lu\n", hdaps_poll_ms); | ||
| 549 | } | ||
| 550 | |||
| 551 | static ssize_t hdaps_poll_store(struct device *dev, | ||
| 552 | struct device_attribute *attr, | ||
| 553 | const char *buf, size_t count) | ||
| 554 | { | ||
| 555 | unsigned int poll; | ||
| 556 | |||
| 557 | if (sscanf(buf, "%u", &poll) != 1 || poll == 0) | ||
| 558 | return -EINVAL; | ||
| 559 | hdaps_poll_ms = poll; | ||
| 560 | |||
| 561 | return count; | ||
| 562 | } | ||
| 563 | |||
| 564 | static ssize_t hdaps_threshold_show(struct device *dev, | ||
| 565 | struct device_attribute *attr, char *buf) | ||
| 566 | { | ||
| 567 | return sprintf(buf, "%u\n", hdaps_mousedev_threshold); | ||
| 568 | } | ||
| 569 | |||
| 570 | static ssize_t hdaps_threshold_store(struct device *dev, | ||
| 571 | struct device_attribute *attr, | ||
| 572 | const char *buf, size_t count) | ||
| 573 | { | ||
| 574 | unsigned int threshold; | ||
| 575 | |||
| 576 | if (sscanf(buf, "%u", &threshold) != 1 || threshold == 0) | ||
| 577 | return -EINVAL; | ||
| 578 | hdaps_mousedev_threshold = threshold; | ||
| 579 | |||
| 580 | return count; | ||
| 581 | } | ||
| 582 | |||
| 583 | static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); | 462 | static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); |
| 584 | static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); | 463 | static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); |
| 585 | static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); | 464 | static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); |
| @@ -588,10 +467,6 @@ static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); | |||
| 588 | static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); | 467 | static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); |
| 589 | static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); | 468 | static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); |
| 590 | static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); | 469 | static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); |
| 591 | static DEVICE_ATTR(mousedev, 0644, hdaps_mousedev_show, hdaps_mousedev_store); | ||
| 592 | static DEVICE_ATTR(mousedev_poll_ms, 0644, hdaps_poll_show, hdaps_poll_store); | ||
| 593 | static DEVICE_ATTR(mousedev_threshold, 0644, hdaps_threshold_show, | ||
| 594 | hdaps_threshold_store); | ||
| 595 | 470 | ||
| 596 | static struct attribute *hdaps_attributes[] = { | 471 | static struct attribute *hdaps_attributes[] = { |
| 597 | &dev_attr_position.attr, | 472 | &dev_attr_position.attr, |
| @@ -601,9 +476,6 @@ static struct attribute *hdaps_attributes[] = { | |||
| 601 | &dev_attr_keyboard_activity.attr, | 476 | &dev_attr_keyboard_activity.attr, |
| 602 | &dev_attr_mouse_activity.attr, | 477 | &dev_attr_mouse_activity.attr, |
| 603 | &dev_attr_calibrate.attr, | 478 | &dev_attr_calibrate.attr, |
| 604 | &dev_attr_mousedev.attr, | ||
| 605 | &dev_attr_mousedev_threshold.attr, | ||
| 606 | &dev_attr_mousedev_poll_ms.attr, | ||
| 607 | &dev_attr_invert.attr, | 479 | &dev_attr_invert.attr, |
| 608 | NULL, | 480 | NULL, |
| 609 | }; | 481 | }; |
| @@ -615,24 +487,19 @@ static struct attribute_group hdaps_attribute_group = { | |||
| 615 | 487 | ||
| 616 | /* Module stuff */ | 488 | /* Module stuff */ |
| 617 | 489 | ||
| 618 | /* | 490 | /* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ |
| 619 | * XXX: We should be able to return nonzero and halt the detection process. | ||
| 620 | * But there is a bug in dmi_check_system() where a nonzero return from the | ||
| 621 | * first match will result in a return of failure from dmi_check_system(). | ||
| 622 | * I fixed this; the patch is in 2.6-mm. Once in Linus's tree we can make | ||
| 623 | * hdaps_dmi_match_invert() return hdaps_dmi_match(), which in turn returns 1. | ||
| 624 | */ | ||
| 625 | static int hdaps_dmi_match(struct dmi_system_id *id) | 491 | static int hdaps_dmi_match(struct dmi_system_id *id) |
| 626 | { | 492 | { |
| 627 | printk(KERN_INFO "hdaps: %s detected.\n", id->ident); | 493 | printk(KERN_INFO "hdaps: %s detected.\n", id->ident); |
| 628 | return 0; | 494 | return 1; |
| 629 | } | 495 | } |
| 630 | 496 | ||
| 497 | /* hdaps_dmi_match_invert - found an inverted match. */ | ||
| 631 | static int hdaps_dmi_match_invert(struct dmi_system_id *id) | 498 | static int hdaps_dmi_match_invert(struct dmi_system_id *id) |
| 632 | { | 499 | { |
| 633 | hdaps_invert = 1; | 500 | hdaps_invert = 1; |
| 634 | printk(KERN_INFO "hdaps: inverting axis readings.\n"); | 501 | printk(KERN_INFO "hdaps: inverting axis readings.\n"); |
| 635 | return 0; | 502 | return hdaps_dmi_match(id); |
| 636 | } | 503 | } |
| 637 | 504 | ||
| 638 | #define HDAPS_DMI_MATCH_NORMAL(model) { \ | 505 | #define HDAPS_DMI_MATCH_NORMAL(model) { \ |
| @@ -662,12 +529,15 @@ static int __init hdaps_init(void) | |||
| 662 | HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), | 529 | HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), |
| 663 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), | 530 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), |
| 664 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), | 531 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), |
| 532 | HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"), | ||
| 665 | HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), | 533 | HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), |
| 666 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), | 534 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), |
| 667 | HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), | 535 | HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), |
| 668 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"), | 536 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"), |
| 669 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), | 537 | HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), |
| 670 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), | 538 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), |
| 539 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"), | ||
| 540 | HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), | ||
| 671 | { .ident = NULL } | 541 | { .ident = NULL } |
| 672 | }; | 542 | }; |
| 673 | 543 | ||
| @@ -696,8 +566,18 @@ static int __init hdaps_init(void) | |||
| 696 | if (ret) | 566 | if (ret) |
| 697 | goto out_device; | 567 | goto out_device; |
| 698 | 568 | ||
| 699 | if (hdaps_mousedev) | 569 | /* initial calibrate for the input device */ |
| 700 | hdaps_mousedev_enable(); | 570 | hdaps_calibrate(); |
| 571 | |||
| 572 | /* initialize the input class */ | ||
| 573 | hdaps_idev.dev = &pdev->dev; | ||
| 574 | input_register_device(&hdaps_idev); | ||
| 575 | |||
| 576 | /* start up our timer for the input device */ | ||
| 577 | init_timer(&hdaps_timer); | ||
| 578 | hdaps_timer.function = hdaps_mousedev_poll; | ||
| 579 | hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD; | ||
| 580 | add_timer(&hdaps_timer); | ||
| 701 | 581 | ||
| 702 | printk(KERN_INFO "hdaps: driver successfully loaded.\n"); | 582 | printk(KERN_INFO "hdaps: driver successfully loaded.\n"); |
| 703 | return 0; | 583 | return 0; |
| @@ -715,8 +595,8 @@ out: | |||
| 715 | 595 | ||
| 716 | static void __exit hdaps_exit(void) | 596 | static void __exit hdaps_exit(void) |
| 717 | { | 597 | { |
| 718 | hdaps_mousedev_disable(); | 598 | del_timer_sync(&hdaps_timer); |
| 719 | 599 | input_unregister_device(&hdaps_idev); | |
| 720 | sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); | 600 | sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); |
| 721 | platform_device_unregister(pdev); | 601 | platform_device_unregister(pdev); |
| 722 | driver_unregister(&hdaps_driver); | 602 | driver_unregister(&hdaps_driver); |
| @@ -728,9 +608,6 @@ static void __exit hdaps_exit(void) | |||
| 728 | module_init(hdaps_init); | 608 | module_init(hdaps_init); |
| 729 | module_exit(hdaps_exit); | 609 | module_exit(hdaps_exit); |
| 730 | 610 | ||
| 731 | module_param_named(mousedev, hdaps_mousedev, bool, 0); | ||
| 732 | MODULE_PARM_DESC(mousedev, "enable the input class device"); | ||
| 733 | |||
| 734 | module_param_named(invert, hdaps_invert, bool, 0); | 611 | module_param_named(invert, hdaps_invert, bool, 0); |
| 735 | MODULE_PARM_DESC(invert, "invert data along each axis"); | 612 | MODULE_PARM_DESC(invert, "invert data along each axis"); |
| 736 | 613 | ||
