1/*
2 * $Id: spaceball.c,v 1.1.1.1 2008/10/15 03:26:32 james26_jang Exp $
3 *
4 *  Copyright (c) 1999-2000 Vojtech Pavlik
5 *
6 *  Based on the work of:
7 *  	David Thompson
8 *  	Joseph Krahn
9 *
10 *  Sponsored by SuSE
11 */
12
13/*
14 * SpaceTec SpaceBall 4000 FLX driver for Linux
15 */
16
17/*
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 *
32 *  Should you need to contact me, the author, you can do so either by
33 * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
34 * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
35 */
36
37#include <linux/kernel.h>
38#include <linux/slab.h>
39#include <linux/module.h>
40#include <linux/init.h>
41#include <linux/input.h>
42#include <linux/serio.h>
43
44/*
45 * Constants.
46 */
47
48#define JS_SBALL_MAX_LENGTH	128
49static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
50static char *spaceball_name = "SpaceTec SpaceBall 4000 FLX";
51
52/*
53 * Per-Ball data.
54 */
55
56struct spaceball {
57	struct input_dev dev;
58	struct serio *serio;
59	int idx;
60	int escape;
61	unsigned char data[JS_SBALL_MAX_LENGTH];
62};
63
64/*
65 * spaceball_process_packet() decodes packets the driver receives from the
66 * SpaceBall.
67 */
68
69static void spaceball_process_packet(struct spaceball* spaceball)
70{
71	struct input_dev *dev = &spaceball->dev;
72	unsigned char *data = spaceball->data;
73	int i;
74
75	if (spaceball->idx < 2) return;
76
77	printk("%c %d\n", spaceball->data[0], spaceball->idx);
78
79	switch (spaceball->data[0]) {
80
81		case '@':					/* Reset packet */
82			spaceball->data[spaceball->idx - 1] = 0;
83			for (i = 1; i < spaceball->idx && spaceball->data[i] == ' '; i++);
84			printk(KERN_INFO "input%d: %s [%s] on serio%d\n",
85				spaceball->dev.number, spaceball_name, spaceball->data + i, spaceball->serio->number);
86			break;
87
88		case 'D':					/* Ball data */
89			if (spaceball->idx != 15) return;
90			for (i = 0; i < 6; i++) {
91				input_report_abs(dev, spaceball_axes[i],
92					(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
93			}
94			break;
95
96		case '.':				/* Button data, part2 */
97			if (spaceball->idx != 3) return;
98			input_report_key(dev, BTN_0,  data[2] & 1);
99			input_report_key(dev, BTN_1, data[2] & 2);
100			break;
101
102		case '?':				/* Error packet */
103			spaceball->data[spaceball->idx - 1] = 0;
104			printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
105			break;
106	}
107}
108
109/*
110 * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
111 * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
112 * can occur in the axis values.
113 */
114
115static void spaceball_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
116{
117	struct spaceball *spaceball = serio->private;
118
119	switch (data) {
120		case 0xd:
121			spaceball_process_packet(spaceball);
122			spaceball->idx = 0;
123			spaceball->escape = 0;
124			return;
125		case '^':
126			if (!spaceball->escape) {
127				spaceball->escape = 1;
128				return;
129			}
130			spaceball->escape = 0;
131		case 'M':
132		case 'Q':
133		case 'S':
134			if (spaceball->escape) {
135				spaceball->escape = 0;
136				data &= 0x1f;
137			}
138		default:
139			if (spaceball->escape) {
140				spaceball->escape = 0;
141				printk(KERN_WARNING "spaceball.c: Unknown escaped character: %#x (%c)\n", data, data);
142			}
143			if (spaceball->idx < JS_SBALL_MAX_LENGTH)
144				spaceball->data[spaceball->idx++] = data;
145			return;
146	}
147}
148
149/*
150 * spaceball_disconnect() is the opposite of spaceball_connect()
151 */
152
153static void spaceball_disconnect(struct serio *serio)
154{
155	struct spaceball* spaceball = serio->private;
156	input_unregister_device(&spaceball->dev);
157	serio_close(serio);
158	kfree(spaceball);
159}
160
161/*
162 * spaceball_connect() is the routine that is called when someone adds a
163 * new serio device. It looks for the Magellan, and if found, registers
164 * it as an input device.
165 */
166
167static void spaceball_connect(struct serio *serio, struct serio_dev *dev)
168{
169	struct spaceball *spaceball;
170	int i, t;
171
172	if (serio->type != (SERIO_RS232 | SERIO_SPACEBALL))
173		return;
174
175	if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL)))
176		return;
177	memset(spaceball, 0, sizeof(struct spaceball));
178
179	spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
180	spaceball->dev.keybit[LONG(BTN_0)] = BIT(BTN_0) | BIT(BTN_1);
181
182	for (i = 0; i < 6; i++) {
183		t = spaceball_axes[i];
184		set_bit(t, spaceball->dev.absbit);
185		spaceball->dev.absmin[t] = i < 3 ? -8000 : -1600;
186		spaceball->dev.absmax[t] = i < 3 ?  8000 :  1600;
187		spaceball->dev.absflat[t] = i < 3 ? 40 : 8;
188		spaceball->dev.absfuzz[t] = i < 3 ? 8 : 2;
189	}
190
191	spaceball->serio = serio;
192	spaceball->dev.private = spaceball;
193
194	spaceball->dev.name = spaceball_name;
195	spaceball->dev.idbus = BUS_RS232;
196	spaceball->dev.idvendor = SERIO_SPACEBALL;
197	spaceball->dev.idproduct = 0x0001;
198	spaceball->dev.idversion = 0x0100;
199
200	serio->private = spaceball;
201
202	if (serio_open(serio, dev)) {
203		kfree(spaceball);
204		return;
205	}
206
207	input_register_device(&spaceball->dev);
208}
209
210/*
211 * The serio device structure.
212 */
213
214static struct serio_dev spaceball_dev = {
215	interrupt:	spaceball_interrupt,
216	connect:	spaceball_connect,
217	disconnect:	spaceball_disconnect,
218};
219
220/*
221 * The functions for inserting/removing us as a module.
222 */
223
224int __init spaceball_init(void)
225{
226	serio_register_device(&spaceball_dev);
227	return 0;
228}
229
230void __exit spaceball_exit(void)
231{
232	serio_unregister_device(&spaceball_dev);
233}
234
235module_init(spaceball_init);
236module_exit(spaceball_exit);
237
238MODULE_LICENSE("GPL");
239