summaryrefslogtreecommitdiffstats
path: root/drivers/char/adbmouse.c
blob: 77bbbbd69a99ad60f510fe12d2620ee2007e7803 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*
 * Macintosh ADB Mouse driver for Linux
 *
 * 27 Oct 1997 Michael Schmitz
 * logitech fixes by anthony tong
 * further hacking by Paul Mackerras
 *
 * Apple mouse protocol according to:
 *
 * Device code shamelessly stolen from:
 */
/*
 * Atari Mouse Driver for Linux
 * by Robert de Vries (robert@and.nl) 19Jul93
 *
 * 16 Nov 1994 Andreas Schwab
 * Compatibility with busmouse
 * Support for three button mouse (shamelessly stolen from MiNT)
 * third button wired to one of the joystick directions on joystick 1
 *
 * 1996/02/11 Andreas Schwab
 * Module support
 * Allow multiple open's
 */

#include <linux/module.h>

#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/init.h>

#include <asm/adb_mouse.h>
#include <asm/uaccess.h>
#ifdef __powerpc__
#include <asm/processor.h>
#endif
#ifdef __mc68000__
#include <asm/setup.h>
#endif

static struct mouse_status mouse;
static unsigned char adb_mouse_buttons[16];

extern void (*adb_mouse_interrupt_hook)(unsigned char *, int);
extern int adb_emulate_buttons;
extern int adb_button2_keycode;
extern int adb_button3_keycode;

extern int console_loglevel;

/*
 *	XXX: need to figure out what ADB mouse packets mean ... 
 *	This is the stuff stolen from the Atari driver ...
 */
static void adb_mouse_interrupt(unsigned char *buf, int nb)
{
    int buttons, id;

/*
    Handler 1 -- 100cpi original Apple mouse protocol.
    Handler 2 -- 200cpi original Apple mouse protocol.

    For Apple's standard one-button mouse protocol the data array will
    contain the following values:

                BITS    COMMENTS
    data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
    data[1] = bxxx xxxx First button and x-axis motion.
    data[2] = byyy yyyy Second button and y-axis motion.

    Handler 4 -- Apple Extended mouse protocol.

    For Apple's 3-button mouse protocol the data array will contain the
    following values:

		BITS    COMMENTS
    data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd.
    data[1] = bxxx xxxx Left button and x-axis motion.
    data[2] = byyy yyyy Second button and y-axis motion.
    data[3] = byyy bxxx Third button and fourth button.  
    	      Y is additional high bits of y-axis motion.  
    	      X is additional high bits of x-axis motion.

    This procedure also gets called from the keyboard code if we
    are emulating mouse buttons with keys.  In this case data[0] == 0
    (data[0] cannot be 0 for a real ADB packet).

    'buttons' here means 'button down' states!
    Button 1 (left)  : bit 2, busmouse button 3
    Button 2 (middle): bit 1, busmouse button 2
    Button 3 (right) : bit 0, busmouse button 1
*/

    /* x/y and buttons swapped */
    
    if (console_loglevel >= 8)
	printk("KERN_DEBUG adb_mouse: %s data; ", buf[0]? "real": "fake"); 

    id = (buf[0] >> 4) & 0xf;
    buttons = adb_mouse_buttons[id];

    /* button 1 (left, bit 2) */
    buttons = (buttons&3) | (buf[1] & 0x80 ? 4 : 0); /* 1+2 unchanged */

    /* button 2 (middle) */
    buttons = (buttons&5) | (buf[2] & 0x80 ? 2 : 0); /* 2+3 unchanged */

    /* button 3 (right) present?
     *  on a logitech mouseman, the right and mid buttons sometimes behave
     *  strangely until they both have been pressed after booting. */
    /* data valid only if extended mouse format ! */
    if (nb >= 4)
	buttons = (buttons&6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */

    add_mouse_randomness(((~buttons&7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f));

    adb_mouse_buttons[id] = buttons;
    /* a button is down if it is down on any mouse */
    for (id = 0; id < 16; ++id)
	buttons &= adb_mouse_buttons[id];

    mouse.buttons = buttons;
    mouse.dx += ((buf[2]&0x7f) < 64 ? (buf[2]&0x7f) : (buf[2]&0x7f)-128 );
    mouse.dy -= ((buf[1]&0x7f) < 64 ? (buf[1]&0x7f) : (buf[1]&0x7f)-128 );

    if (console_loglevel >= 8)
        printk(" %X %X %X buttons %x dx %d dy %d \n", 
               buf[1], buf[2], buf[3], mouse.buttons, mouse.dx, mouse.dy);
 
    mouse.ready = 1;
    wake_up_interruptible(&mouse.wait);
    if (mouse.fasyncptr)
	kill_fasync(mouse.fasyncptr, SIGIO);
}

static int fasync_mouse(int fd, struct file *filp, int on)
{
	int retval;

	retval = fasync_helper(fd, filp, on, &mouse.fasyncptr);
	if (retval < 0)
		return retval;
	return 0;
}

static int release_mouse(struct inode *inode, struct file *file)
{
    fasync_mouse(-1, file, 0);
    if (--mouse.active)
      return 0;

    adb_mouse_interrupt_hook = NULL;
    MOD_DEC_USE_COUNT;
    return 0;
}

static int open_mouse(struct inode *inode, struct file *file)
{
    int id;

    if (mouse.active++)
	return 0;
	
    mouse.ready = 0;

    mouse.dx = mouse.dy = 0;
    for (id = 0; id < 16; ++id)
	adb_mouse_buttons[id] = 7;	/* all buttons up */
    MOD_INC_USE_COUNT;
    adb_mouse_interrupt_hook = adb_mouse_interrupt;
    return 0;
}

static ssize_t write_mouse(struct file *file, const char *buffer,
			   size_t count, loff_t *ppos)
{
    return -EINVAL;
}

static ssize_t read_mouse(struct file *file, char *buffer, size_t count,
			  loff_t *ppos)
{
    int dx, dy, buttons;

    if (count < 3)
	return -EINVAL;
    if (!mouse.ready)
	return -EAGAIN;
    dx = mouse.dx;
    dy = mouse.dy;
    buttons = mouse.buttons;
    if (dx > 127)
	dx = 127;
    else if (dx < -128)
	dx = -128;
    if (dy > 127)
	dy = 127;
    else if (dy < -128)
	dy = -128;
    mouse.dx -= dx;
    mouse.dy -= dy;
    if (mouse.dx == 0 && mouse.dy == 0)
	mouse.ready = 0;
    if (put_user(buttons | 0x80, buffer++) ||
	put_user((char) dx, buffer++) ||
	put_user((char) dy, buffer++))
	return -EFAULT;
    if (count > 3)
	if (clear_user(buffer, count - 3))
	    return -EFAULT;
    return count;
}

static unsigned int mouse_poll(struct file *file, poll_table *wait)
{
	poll_wait(file, &mouse.wait, wait);
	if (mouse.ready)
		return POLLIN | POLLRDNORM;
	return 0;
}

struct file_operations adb_mouse_fops = {
    NULL,		/* mouse_seek */
    read_mouse,
    write_mouse,
    NULL,		/* mouse_readdir */
    mouse_poll,
    NULL,		/* mouse_ioctl */
    NULL,		/* mouse_mmap */
    open_mouse,
    NULL,		/* flush */
    release_mouse,
    NULL,
    fasync_mouse,
};

#define ADB_MOUSE_MINOR	10

static struct miscdevice adb_mouse = {
    ADB_MOUSE_MINOR, "adbmouse", &adb_mouse_fops
};

__initfunc(int adb_mouse_init(void))
{
    mouse.active = 0;
    mouse.ready = 0;
    init_waitqueue_head(&mouse.wait);

#ifdef __powerpc__
    if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
	    return -ENODEV;
#endif
#ifdef __mc68000__
    if (!MACH_IS_MAC)
	return -ENODEV;
#endif
    printk(KERN_INFO "Macintosh ADB mouse driver installed.\n");
    misc_register(&adb_mouse);
    return 0;
}


/*
 * XXX this function is misnamed.
 * It is called if the kernel is booted with the adb_buttons=xxx
 * option, which is about using ADB keyboard buttons to emulate
 * mouse buttons. -- paulus
 */
__initfunc(void adb_mouse_setup(char *str, int *ints))
{
	if (ints[0] >= 1) {
		adb_emulate_buttons = ints[1] > 0;
		if (ints[1] > 1)
			adb_button2_keycode = ints[1];
		if (ints[0] >= 2)
			adb_button3_keycode = ints[2];
	}
}

#ifdef MODULE
#include <asm/setup.h>

int init_module(void)
{
    return adb_mouse_init();
}

void cleanup_module(void)
{
    misc_deregister(&adb_mouse);
}
#endif