Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Generic linux-input device driver for axis-bearing devices | |
3 | * | |
4 | * Copyright (c) 2001 Brian S. Julin | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions, and the following disclaimer, | |
12 | * without modification. | |
13 | * 2. The name of the author may not be used to endorse or promote products | |
14 | * derived from this software without specific prior written permission. | |
15 | * | |
16 | * Alternatively, this software may be distributed under the terms of the | |
17 | * GNU General Public License ("GPL"). | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR | |
23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
28 | * | |
29 | * References: | |
30 | * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A | |
31 | * | |
32 | */ | |
33 | ||
34 | #include <linux/hil.h> | |
35 | #include <linux/input.h> | |
36 | #include <linux/serio.h> | |
37 | #include <linux/kernel.h> | |
38 | #include <linux/module.h> | |
39 | #include <linux/init.h> | |
40 | #include <linux/slab.h> | |
41 | #include <linux/pci_ids.h> | |
42 | ||
43 | #define PREFIX "HIL PTR: " | |
44 | #define HIL_GENERIC_NAME "HIL pointer device" | |
45 | ||
46 | MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); | |
47 | MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver"); | |
48 | MODULE_LICENSE("Dual BSD/GPL"); | |
49 | ||
50 | ||
51 | #define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */ | |
52 | #undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */ | |
53 | ||
54 | ||
55 | #define HIL_PTR_MAX_LENGTH 16 | |
56 | ||
57 | struct hil_ptr { | |
102c8c76 | 58 | struct input_dev *dev; |
1da177e4 LT |
59 | struct serio *serio; |
60 | ||
61 | /* Input buffer and index for packets from HIL bus. */ | |
62 | hil_packet data[HIL_PTR_MAX_LENGTH]; | |
63 | int idx4; /* four counts per packet */ | |
64 | ||
65 | /* Raw device info records from HIL bus, see hil.h for fields. */ | |
66 | char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */ | |
67 | char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */ | |
68 | char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */ | |
69 | char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */ | |
70 | ||
71 | /* Extra device details not contained in struct input_dev. */ | |
72 | unsigned int nbtn, naxes; | |
73 | unsigned int btnmap[7]; | |
74 | ||
75 | /* Something to sleep around with. */ | |
76 | struct semaphore sem; | |
77 | }; | |
78 | ||
79 | /* Process a complete packet after transfer from the HIL */ | |
80 | static void hil_ptr_process_record(struct hil_ptr *ptr) | |
81 | { | |
102c8c76 | 82 | struct input_dev *dev = ptr->dev; |
1da177e4 LT |
83 | hil_packet *data = ptr->data; |
84 | hil_packet p; | |
85 | int idx, i, cnt, laxis; | |
86 | int ax16, absdev; | |
87 | ||
88 | idx = ptr->idx4/4; | |
89 | p = data[idx - 1]; | |
90 | ||
ffd51f46 HD |
91 | if ((p & ~HIL_CMDCT_POL) == |
92 | (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) | |
93 | goto report; | |
94 | if ((p & ~HIL_CMDCT_RPL) == | |
95 | (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) | |
96 | goto report; | |
1da177e4 LT |
97 | |
98 | /* Not a poll response. See if we are loading config records. */ | |
99 | switch (p & HIL_PKT_DATA_MASK) { | |
100 | case HIL_CMD_IDD: | |
101 | for (i = 0; i < idx; i++) | |
102 | ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | |
103 | for (; i < HIL_PTR_MAX_LENGTH; i++) | |
104 | ptr->idd[i] = 0; | |
105 | break; | |
ffd51f46 | 106 | |
1da177e4 LT |
107 | case HIL_CMD_RSC: |
108 | for (i = 0; i < idx; i++) | |
109 | ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | |
110 | for (; i < HIL_PTR_MAX_LENGTH; i++) | |
111 | ptr->rsc[i] = 0; | |
112 | break; | |
ffd51f46 | 113 | |
1da177e4 LT |
114 | case HIL_CMD_EXD: |
115 | for (i = 0; i < idx; i++) | |
116 | ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | |
117 | for (; i < HIL_PTR_MAX_LENGTH; i++) | |
118 | ptr->exd[i] = 0; | |
119 | break; | |
ffd51f46 | 120 | |
1da177e4 LT |
121 | case HIL_CMD_RNM: |
122 | for (i = 0; i < idx; i++) | |
123 | ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK; | |
124 | for (; i < HIL_PTR_MAX_LENGTH + 1; i++) | |
ffd51f46 | 125 | ptr->rnm[i] = 0; |
1da177e4 | 126 | break; |
ffd51f46 | 127 | |
1da177e4 LT |
128 | default: |
129 | /* These occur when device isn't present */ | |
ffd51f46 HD |
130 | if (p == (HIL_ERR_INT | HIL_PKT_CMD)) |
131 | break; | |
1da177e4 LT |
132 | /* Anything else we'd like to know about. */ |
133 | printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); | |
134 | break; | |
135 | } | |
136 | goto out; | |
137 | ||
138 | report: | |
139 | if ((p & HIL_CMDCT_POL) != idx - 1) { | |
ffd51f46 HD |
140 | printk(KERN_WARNING PREFIX |
141 | "Malformed poll packet %x (idx = %i)\n", p, idx); | |
1da177e4 LT |
142 | goto out; |
143 | } | |
144 | ||
145 | i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0; | |
146 | laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK; | |
147 | laxis += i; | |
148 | ||
149 | ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ | |
ffd51f46 | 150 | absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; |
1da177e4 LT |
151 | |
152 | for (cnt = 1; i < laxis; i++) { | |
153 | unsigned int lo,hi,val; | |
154 | lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK; | |
155 | hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0; | |
156 | if (absdev) { | |
157 | val = lo + (hi<<8); | |
158 | #ifdef TABLET_AUTOADJUST | |
102c8c76 HD |
159 | if (val < dev->absmin[ABS_X + i]) |
160 | dev->absmin[ABS_X + i] = val; | |
161 | if (val > dev->absmax[ABS_X + i]) | |
162 | dev->absmax[ABS_X + i] = val; | |
1da177e4 | 163 | #endif |
102c8c76 | 164 | if (i%3) val = dev->absmax[ABS_X + i] - val; |
1da177e4 LT |
165 | input_report_abs(dev, ABS_X + i, val); |
166 | } else { | |
167 | val = (int) (((int8_t)lo) | ((int8_t)hi<<8)); | |
ffd51f46 HD |
168 | if (i%3) |
169 | val *= -1; | |
1da177e4 LT |
170 | input_report_rel(dev, REL_X + i, val); |
171 | } | |
172 | } | |
173 | ||
174 | while (cnt < idx - 1) { | |
175 | unsigned int btn; | |
176 | int up; | |
177 | btn = ptr->data[cnt++]; | |
178 | up = btn & 1; | |
179 | btn &= 0xfe; | |
ffd51f46 | 180 | if (btn == 0x8e) |
1da177e4 | 181 | continue; /* TODO: proximity == touch? */ |
ffd51f46 HD |
182 | else |
183 | if ((btn > 0x8c) || (btn < 0x80)) | |
184 | continue; | |
1da177e4 LT |
185 | btn = (btn - 0x80) >> 1; |
186 | btn = ptr->btnmap[btn]; | |
187 | input_report_key(dev, btn, !up); | |
188 | } | |
189 | input_sync(dev); | |
190 | out: | |
191 | ptr->idx4 = 0; | |
192 | up(&ptr->sem); | |
193 | } | |
194 | ||
ffd51f46 HD |
195 | static void hil_ptr_process_err(struct hil_ptr *ptr) |
196 | { | |
1da177e4 LT |
197 | printk(KERN_WARNING PREFIX "errored HIL packet\n"); |
198 | ptr->idx4 = 0; | |
199 | up(&ptr->sem); | |
1da177e4 LT |
200 | } |
201 | ||
ffd51f46 | 202 | static irqreturn_t hil_ptr_interrupt(struct serio *serio, |
7d12e780 | 203 | unsigned char data, unsigned int flags) |
1da177e4 LT |
204 | { |
205 | struct hil_ptr *ptr; | |
206 | hil_packet packet; | |
207 | int idx; | |
208 | ||
6ab0f5cd | 209 | ptr = serio_get_drvdata(serio); |
ffd51f46 | 210 | BUG_ON(ptr == NULL); |
1da177e4 LT |
211 | |
212 | if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) { | |
213 | hil_ptr_process_err(ptr); | |
214 | return IRQ_HANDLED; | |
215 | } | |
216 | idx = ptr->idx4/4; | |
ffd51f46 HD |
217 | if (!(ptr->idx4 % 4)) |
218 | ptr->data[idx] = 0; | |
1da177e4 LT |
219 | packet = ptr->data[idx]; |
220 | packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8); | |
221 | ptr->data[idx] = packet; | |
222 | ||
223 | /* Records of N 4-byte hil_packets must terminate with a command. */ | |
ffd51f46 HD |
224 | if ((++(ptr->idx4)) % 4) |
225 | return IRQ_HANDLED; | |
1da177e4 LT |
226 | if ((packet & 0xffff0000) != HIL_ERR_INT) { |
227 | hil_ptr_process_err(ptr); | |
228 | return IRQ_HANDLED; | |
229 | } | |
ffd51f46 | 230 | if (packet & HIL_PKT_CMD) |
1da177e4 | 231 | hil_ptr_process_record(ptr); |
ffd51f46 | 232 | |
1da177e4 LT |
233 | return IRQ_HANDLED; |
234 | } | |
235 | ||
236 | static void hil_ptr_disconnect(struct serio *serio) | |
237 | { | |
238 | struct hil_ptr *ptr; | |
239 | ||
6ab0f5cd | 240 | ptr = serio_get_drvdata(serio); |
ffd51f46 | 241 | BUG_ON(ptr == NULL); |
1da177e4 | 242 | |
1da177e4 | 243 | serio_close(serio); |
cd7a9202 | 244 | input_unregister_device(ptr->dev); |
1da177e4 LT |
245 | kfree(ptr); |
246 | } | |
247 | ||
6ab0f5cd | 248 | static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) |
1da177e4 | 249 | { |
2ff98147 AS |
250 | struct hil_ptr *ptr; |
251 | const char *txt; | |
252 | unsigned int i, naxsets, btntype; | |
253 | uint8_t did, *idd; | |
254 | int error; | |
255 | ||
256 | ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL); | |
257 | if (!ptr) | |
b39787a9 | 258 | return -ENOMEM; |
1da177e4 | 259 | |
102c8c76 | 260 | ptr->dev = input_allocate_device(); |
2ff98147 AS |
261 | if (!ptr->dev) { |
262 | error = -ENOMEM; | |
b39787a9 | 263 | goto bail0; |
2ff98147 | 264 | } |
1da177e4 | 265 | |
2ff98147 AS |
266 | error = serio_open(serio, driver); |
267 | if (error) | |
95d465fd | 268 | goto bail1; |
1da177e4 | 269 | |
6ab0f5cd | 270 | serio_set_drvdata(serio, ptr); |
1da177e4 | 271 | ptr->serio = serio; |
1da177e4 | 272 | |
ffd51f46 | 273 | init_MUTEX_LOCKED(&ptr->sem); |
1da177e4 LT |
274 | |
275 | /* Get device info. MLC driver supplies devid/status/etc. */ | |
276 | serio->write(serio, 0); | |
277 | serio->write(serio, 0); | |
278 | serio->write(serio, HIL_PKT_CMD >> 8); | |
279 | serio->write(serio, HIL_CMD_IDD); | |
ffd51f46 | 280 | down(&ptr->sem); |
1da177e4 LT |
281 | |
282 | serio->write(serio, 0); | |
283 | serio->write(serio, 0); | |
284 | serio->write(serio, HIL_PKT_CMD >> 8); | |
285 | serio->write(serio, HIL_CMD_RSC); | |
ffd51f46 | 286 | down(&ptr->sem); |
1da177e4 LT |
287 | |
288 | serio->write(serio, 0); | |
289 | serio->write(serio, 0); | |
290 | serio->write(serio, HIL_PKT_CMD >> 8); | |
291 | serio->write(serio, HIL_CMD_RNM); | |
ffd51f46 | 292 | down(&ptr->sem); |
1da177e4 LT |
293 | |
294 | serio->write(serio, 0); | |
295 | serio->write(serio, 0); | |
296 | serio->write(serio, HIL_PKT_CMD >> 8); | |
297 | serio->write(serio, HIL_CMD_EXD); | |
ffd51f46 | 298 | down(&ptr->sem); |
1da177e4 | 299 | |
ffd51f46 | 300 | up(&ptr->sem); |
1da177e4 | 301 | |
1da177e4 LT |
302 | did = ptr->idd[0]; |
303 | idd = ptr->idd + 1; | |
304 | txt = "unknown"; | |
2ff98147 | 305 | |
1da177e4 | 306 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { |
7b19ada2 | 307 | ptr->dev->evbit[0] = BIT_MASK(EV_REL); |
1da177e4 LT |
308 | txt = "relative"; |
309 | } | |
310 | ||
311 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) { | |
7b19ada2 | 312 | ptr->dev->evbit[0] = BIT_MASK(EV_ABS); |
1da177e4 LT |
313 | txt = "absolute"; |
314 | } | |
2ff98147 AS |
315 | |
316 | if (!ptr->dev->evbit[0]) { | |
317 | error = -ENODEV; | |
102c8c76 | 318 | goto bail2; |
2ff98147 | 319 | } |
1da177e4 LT |
320 | |
321 | ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); | |
ffd51f46 | 322 | if (ptr->nbtn) |
7b19ada2 | 323 | ptr->dev->evbit[0] |= BIT_MASK(EV_KEY); |
1da177e4 LT |
324 | |
325 | naxsets = HIL_IDD_NUM_AXSETS(*idd); | |
326 | ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); | |
327 | ||
328 | printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n", | |
329 | did, txt); | |
330 | printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n", | |
331 | ptr->nbtn, naxsets, ptr->naxes); | |
ffd51f46 | 332 | |
1da177e4 LT |
333 | btntype = BTN_MISC; |
334 | if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) | |
335 | #ifdef TABLET_SIMULATES_MOUSE | |
336 | btntype = BTN_TOUCH; | |
337 | #else | |
338 | btntype = BTN_DIGI; | |
339 | #endif | |
340 | if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) | |
341 | btntype = BTN_TOUCH; | |
ffd51f46 | 342 | |
1da177e4 LT |
343 | if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) |
344 | btntype = BTN_MOUSE; | |
345 | ||
346 | for (i = 0; i < ptr->nbtn; i++) { | |
102c8c76 | 347 | set_bit(btntype | i, ptr->dev->keybit); |
1da177e4 LT |
348 | ptr->btnmap[i] = btntype | i; |
349 | } | |
350 | ||
351 | if (btntype == BTN_MOUSE) { | |
352 | /* Swap buttons 2 and 3 */ | |
353 | ptr->btnmap[1] = BTN_MIDDLE; | |
354 | ptr->btnmap[2] = BTN_RIGHT; | |
355 | } | |
356 | ||
357 | if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { | |
ffd51f46 | 358 | for (i = 0; i < ptr->naxes; i++) |
102c8c76 | 359 | set_bit(REL_X + i, ptr->dev->relbit); |
ffd51f46 | 360 | for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) |
102c8c76 | 361 | set_bit(REL_X + i, ptr->dev->relbit); |
1da177e4 LT |
362 | } else { |
363 | for (i = 0; i < ptr->naxes; i++) { | |
102c8c76 HD |
364 | set_bit(ABS_X + i, ptr->dev->absbit); |
365 | ptr->dev->absmin[ABS_X + i] = 0; | |
366 | ptr->dev->absmax[ABS_X + i] = | |
1da177e4 LT |
367 | HIL_IDD_AXIS_MAX((ptr->idd + 1), i); |
368 | } | |
369 | for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { | |
102c8c76 HD |
370 | set_bit(ABS_X + i, ptr->dev->absbit); |
371 | ptr->dev->absmin[ABS_X + i] = 0; | |
372 | ptr->dev->absmax[ABS_X + i] = | |
1da177e4 LT |
373 | HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3)); |
374 | } | |
375 | #ifdef TABLET_AUTOADJUST | |
376 | for (i = 0; i < ABS_MAX; i++) { | |
102c8c76 HD |
377 | int diff = ptr->dev->absmax[ABS_X + i] / 10; |
378 | ptr->dev->absmin[ABS_X + i] += diff; | |
379 | ptr->dev->absmax[ABS_X + i] -= diff; | |
1da177e4 LT |
380 | } |
381 | #endif | |
382 | } | |
383 | ||
102c8c76 | 384 | ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME; |
1da177e4 | 385 | |
102c8c76 HD |
386 | ptr->dev->id.bustype = BUS_HIL; |
387 | ptr->dev->id.vendor = PCI_VENDOR_ID_HP; | |
388 | ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */ | |
389 | ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */ | |
28aa7f1c | 390 | ptr->dev->dev.parent = &serio->dev; |
1da177e4 | 391 | |
2ff98147 AS |
392 | error = input_register_device(ptr->dev); |
393 | if (error) { | |
394 | printk(KERN_INFO PREFIX "Unable to register input device\n"); | |
395 | goto bail2; | |
396 | } | |
397 | ||
1da177e4 | 398 | printk(KERN_INFO "input: %s (%s), ID: %d\n", |
102c8c76 | 399 | ptr->dev->name, |
1da177e4 LT |
400 | (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad", |
401 | did); | |
402 | ||
6ab0f5cd | 403 | return 0; |
2ff98147 | 404 | |
102c8c76 | 405 | bail2: |
1da177e4 | 406 | serio_close(serio); |
102c8c76 HD |
407 | bail1: |
408 | input_free_device(ptr->dev); | |
1da177e4 LT |
409 | bail0: |
410 | kfree(ptr); | |
6ab0f5cd | 411 | serio_set_drvdata(serio, NULL); |
2ff98147 | 412 | return error; |
1da177e4 LT |
413 | } |
414 | ||
6ab0f5cd MW |
415 | static struct serio_device_id hil_ptr_ids[] = { |
416 | { | |
417 | .type = SERIO_HIL_MLC, | |
418 | .proto = SERIO_HIL, | |
419 | .id = SERIO_ANY, | |
420 | .extra = SERIO_ANY, | |
421 | }, | |
422 | { 0 } | |
423 | }; | |
1da177e4 LT |
424 | |
425 | static struct serio_driver hil_ptr_serio_driver = { | |
426 | .driver = { | |
427 | .name = "hil_ptr", | |
428 | }, | |
429 | .description = "HP HIL mouse/tablet driver", | |
6ab0f5cd MW |
430 | .id_table = hil_ptr_ids, |
431 | .connect = hil_ptr_connect, | |
432 | .disconnect = hil_ptr_disconnect, | |
433 | .interrupt = hil_ptr_interrupt | |
1da177e4 LT |
434 | }; |
435 | ||
436 | static int __init hil_ptr_init(void) | |
437 | { | |
153a9df0 | 438 | return serio_register_driver(&hil_ptr_serio_driver); |
1da177e4 | 439 | } |
ffd51f46 | 440 | |
1da177e4 LT |
441 | static void __exit hil_ptr_exit(void) |
442 | { | |
443 | serio_unregister_driver(&hil_ptr_serio_driver); | |
444 | } | |
ffd51f46 | 445 | |
1da177e4 LT |
446 | module_init(hil_ptr_init); |
447 | module_exit(hil_ptr_exit); |