diff options
Diffstat (limited to 'drivers/input/joystick/twidjoy.c')
-rw-r--r-- | drivers/input/joystick/twidjoy.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c new file mode 100644 index 000000000000..0379bc166525 --- /dev/null +++ b/drivers/input/joystick/twidjoy.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $ | ||
3 | * | ||
4 | * derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp" | ||
5 | * | ||
6 | * Copyright (c) 2001 Arndt Schoenewald | ||
7 | * Copyright (c) 2000-2001 Vojtech Pavlik | ||
8 | * Copyright (c) 2000 Mark Fletcher | ||
9 | * | ||
10 | * Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Driver to use Handykey's Twiddler (the first edition, i.e. the one with | ||
15 | * the RS232 interface) as a joystick under Linux | ||
16 | * | ||
17 | * The Twiddler is a one-handed chording keyboard featuring twelve buttons on | ||
18 | * the front, six buttons on the top, and a built-in tilt sensor. The buttons | ||
19 | * on the front, which are grouped as four rows of three buttons, are pressed | ||
20 | * by the four fingers (this implies only one button per row can be held down | ||
21 | * at the same time) and the buttons on the top are for the thumb. The tilt | ||
22 | * sensor delivers X and Y axis data depending on how the Twiddler is held. | ||
23 | * Additional information can be found at http://www.handykey.com. | ||
24 | * | ||
25 | * This driver does not use the Twiddler for its intended purpose, i.e. as | ||
26 | * a chording keyboard, but as a joystick: pressing and releasing a button | ||
27 | * immediately sends a corresponding button event, and tilting it generates | ||
28 | * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game | ||
29 | * controller with amazing 18 buttons :-) | ||
30 | * | ||
31 | * Note: The Twiddler2 (the successor of the Twiddler that connects directly | ||
32 | * to the PS/2 keyboard and mouse ports) is NOT supported by this driver! | ||
33 | * | ||
34 | * For questions or feedback regarding this driver module please contact: | ||
35 | * Arndt Schoenewald <arndt@quelltext.com> | ||
36 | */ | ||
37 | |||
38 | /* | ||
39 | * This program is free software; you can redistribute it and/or modify | ||
40 | * it under the terms of the GNU General Public License as published by | ||
41 | * the Free Software Foundation; either version 2 of the License, or | ||
42 | * (at your option) any later version. | ||
43 | * | ||
44 | * This program is distributed in the hope that it will be useful, | ||
45 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
46 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
47 | * GNU General Public License for more details. | ||
48 | * | ||
49 | * You should have received a copy of the GNU General Public License | ||
50 | * along with this program; if not, write to the Free Software | ||
51 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
52 | */ | ||
53 | |||
54 | #include <linux/kernel.h> | ||
55 | #include <linux/module.h> | ||
56 | #include <linux/slab.h> | ||
57 | #include <linux/input.h> | ||
58 | #include <linux/serio.h> | ||
59 | #include <linux/init.h> | ||
60 | |||
61 | #define DRIVER_DESC "Handykey Twiddler keyboard as a joystick driver" | ||
62 | |||
63 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
64 | MODULE_LICENSE("GPL"); | ||
65 | |||
66 | /* | ||
67 | * Constants. | ||
68 | */ | ||
69 | |||
70 | #define TWIDJOY_MAX_LENGTH 5 | ||
71 | |||
72 | static char *twidjoy_name = "Handykey Twiddler"; | ||
73 | |||
74 | static struct twidjoy_button_spec { | ||
75 | int bitshift; | ||
76 | int bitmask; | ||
77 | int buttons[3]; | ||
78 | } | ||
79 | twidjoy_buttons[] = { | ||
80 | { 0, 3, { BTN_A, BTN_B, BTN_C } }, | ||
81 | { 2, 3, { BTN_X, BTN_Y, BTN_Z } }, | ||
82 | { 4, 3, { BTN_TL, BTN_TR, BTN_TR2 } }, | ||
83 | { 6, 3, { BTN_SELECT, BTN_START, BTN_MODE } }, | ||
84 | { 8, 1, { BTN_BASE5 } }, | ||
85 | { 9, 1, { BTN_BASE } }, | ||
86 | { 10, 1, { BTN_BASE3 } }, | ||
87 | { 11, 1, { BTN_BASE4 } }, | ||
88 | { 12, 1, { BTN_BASE2 } }, | ||
89 | { 13, 1, { BTN_BASE6 } }, | ||
90 | { 0, 0, { 0 } } | ||
91 | }; | ||
92 | |||
93 | /* | ||
94 | * Per-Twiddler data. | ||
95 | */ | ||
96 | |||
97 | struct twidjoy { | ||
98 | struct input_dev dev; | ||
99 | int idx; | ||
100 | unsigned char data[TWIDJOY_MAX_LENGTH]; | ||
101 | char phys[32]; | ||
102 | }; | ||
103 | |||
104 | /* | ||
105 | * twidjoy_process_packet() decodes packets the driver receives from the | ||
106 | * Twiddler. It updates the data accordingly. | ||
107 | */ | ||
108 | |||
109 | static void twidjoy_process_packet(struct twidjoy *twidjoy, struct pt_regs *regs) | ||
110 | { | ||
111 | if (twidjoy->idx == TWIDJOY_MAX_LENGTH) { | ||
112 | struct input_dev *dev = &twidjoy->dev; | ||
113 | unsigned char *data = twidjoy->data; | ||
114 | struct twidjoy_button_spec *bp; | ||
115 | int button_bits, abs_x, abs_y; | ||
116 | |||
117 | button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f); | ||
118 | |||
119 | input_regs(dev, regs); | ||
120 | |||
121 | for (bp = twidjoy_buttons; bp->bitmask; bp++) { | ||
122 | int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift; | ||
123 | int i; | ||
124 | |||
125 | for (i = 0; i < bp->bitmask; i++) | ||
126 | input_report_key(dev, bp->buttons[i], i+1 == value); | ||
127 | } | ||
128 | |||
129 | abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2); | ||
130 | if (data[4] & 0x08) abs_x -= 256; | ||
131 | |||
132 | abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0); | ||
133 | if (data[3] & 0x02) abs_y -= 256; | ||
134 | |||
135 | input_report_abs(dev, ABS_X, -abs_x); | ||
136 | input_report_abs(dev, ABS_Y, +abs_y); | ||
137 | |||
138 | input_sync(dev); | ||
139 | } | ||
140 | |||
141 | return; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * twidjoy_interrupt() is called by the low level driver when characters | ||
146 | * are ready for us. We then buffer them for further processing, or call the | ||
147 | * packet processing routine. | ||
148 | */ | ||
149 | |||
150 | static irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) | ||
151 | { | ||
152 | struct twidjoy *twidjoy = serio_get_drvdata(serio); | ||
153 | |||
154 | /* All Twiddler packets are 5 bytes. The fact that the first byte | ||
155 | * has a MSB of 0 and all other bytes have a MSB of 1 can be used | ||
156 | * to check and regain sync. */ | ||
157 | |||
158 | if ((data & 0x80) == 0) | ||
159 | twidjoy->idx = 0; /* this byte starts a new packet */ | ||
160 | else if (twidjoy->idx == 0) | ||
161 | return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ | ||
162 | |||
163 | if (twidjoy->idx < TWIDJOY_MAX_LENGTH) | ||
164 | twidjoy->data[twidjoy->idx++] = data; | ||
165 | |||
166 | if (twidjoy->idx == TWIDJOY_MAX_LENGTH) { | ||
167 | twidjoy_process_packet(twidjoy, regs); | ||
168 | twidjoy->idx = 0; | ||
169 | } | ||
170 | |||
171 | return IRQ_HANDLED; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * twidjoy_disconnect() is the opposite of twidjoy_connect() | ||
176 | */ | ||
177 | |||
178 | static void twidjoy_disconnect(struct serio *serio) | ||
179 | { | ||
180 | struct twidjoy *twidjoy = serio_get_drvdata(serio); | ||
181 | |||
182 | input_unregister_device(&twidjoy->dev); | ||
183 | serio_close(serio); | ||
184 | serio_set_drvdata(serio, NULL); | ||
185 | kfree(twidjoy); | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * twidjoy_connect() is the routine that is called when someone adds a | ||
190 | * new serio device. It looks for the Twiddler, and if found, registers | ||
191 | * it as an input device. | ||
192 | */ | ||
193 | |||
194 | static int twidjoy_connect(struct serio *serio, struct serio_driver *drv) | ||
195 | { | ||
196 | struct twidjoy_button_spec *bp; | ||
197 | struct twidjoy *twidjoy; | ||
198 | int i; | ||
199 | int err; | ||
200 | |||
201 | if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL))) | ||
202 | return -ENOMEM; | ||
203 | |||
204 | memset(twidjoy, 0, sizeof(struct twidjoy)); | ||
205 | |||
206 | sprintf(twidjoy->phys, "%s/input0", serio->phys); | ||
207 | |||
208 | init_input_dev(&twidjoy->dev); | ||
209 | twidjoy->dev.name = twidjoy_name; | ||
210 | twidjoy->dev.phys = twidjoy->phys; | ||
211 | twidjoy->dev.id.bustype = BUS_RS232; | ||
212 | twidjoy->dev.id.vendor = SERIO_TWIDJOY; | ||
213 | twidjoy->dev.id.product = 0x0001; | ||
214 | twidjoy->dev.id.version = 0x0100; | ||
215 | twidjoy->dev.dev = &serio->dev; | ||
216 | |||
217 | twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); | ||
218 | |||
219 | for (bp = twidjoy_buttons; bp->bitmask; bp++) { | ||
220 | for (i = 0; i < bp->bitmask; i++) | ||
221 | set_bit(bp->buttons[i], twidjoy->dev.keybit); | ||
222 | } | ||
223 | |||
224 | twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); | ||
225 | |||
226 | for (i = 0; i < 2; i++) { | ||
227 | twidjoy->dev.absmax[ABS_X+i] = 50; | ||
228 | twidjoy->dev.absmin[ABS_X+i] = -50; | ||
229 | |||
230 | /* TODO: arndt 20010708: Are these values appropriate? */ | ||
231 | twidjoy->dev.absfuzz[ABS_X+i] = 4; | ||
232 | twidjoy->dev.absflat[ABS_X+i] = 4; | ||
233 | } | ||
234 | |||
235 | twidjoy->dev.private = twidjoy; | ||
236 | |||
237 | serio_set_drvdata(serio, twidjoy); | ||
238 | |||
239 | err = serio_open(serio, drv); | ||
240 | if (err) { | ||
241 | serio_set_drvdata(serio, NULL); | ||
242 | kfree(twidjoy); | ||
243 | return err; | ||
244 | } | ||
245 | |||
246 | input_register_device(&twidjoy->dev); | ||
247 | |||
248 | printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys); | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * The serio driver structure. | ||
255 | */ | ||
256 | |||
257 | static struct serio_device_id twidjoy_serio_ids[] = { | ||
258 | { | ||
259 | .type = SERIO_RS232, | ||
260 | .proto = SERIO_TWIDJOY, | ||
261 | .id = SERIO_ANY, | ||
262 | .extra = SERIO_ANY, | ||
263 | }, | ||
264 | { 0 } | ||
265 | }; | ||
266 | |||
267 | MODULE_DEVICE_TABLE(serio, twidjoy_serio_ids); | ||
268 | |||
269 | static struct serio_driver twidjoy_drv = { | ||
270 | .driver = { | ||
271 | .name = "twidjoy", | ||
272 | }, | ||
273 | .description = DRIVER_DESC, | ||
274 | .id_table = twidjoy_serio_ids, | ||
275 | .interrupt = twidjoy_interrupt, | ||
276 | .connect = twidjoy_connect, | ||
277 | .disconnect = twidjoy_disconnect, | ||
278 | }; | ||
279 | |||
280 | /* | ||
281 | * The functions for inserting/removing us as a module. | ||
282 | */ | ||
283 | |||
284 | int __init twidjoy_init(void) | ||
285 | { | ||
286 | serio_register_driver(&twidjoy_drv); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | void __exit twidjoy_exit(void) | ||
291 | { | ||
292 | serio_unregister_driver(&twidjoy_drv); | ||
293 | } | ||
294 | |||
295 | module_init(twidjoy_init); | ||
296 | module_exit(twidjoy_exit); | ||