summaryrefslogtreecommitdiffstats
path: root/drivers/char/macmouse.c
blob: 2d7145203b828f31e4bed87fa61767120de533e4 (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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/*
 * Macintosh ADB Mouse driver for Linux
 *
 * 27 Oct 1997 Michael Schmitz
 *
 * 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/setup.h>
#include <asm/mac_mouse.h>
#include <asm/segment.h>
#include <asm/uaccess.h>

static struct mouse_status mouse;
static int mac_mouse_x_threshold = 2, mac_mouse_y_threshold = 2;
static int mac_mouse_buttons = 0;

extern void (*mac_mouse_interrupt_hook) (char *, int);
extern int mac_emulate_button2;
extern int mac_emulate_button3;

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 mac_mouse_interrupt(char *buf, int nb)
{
    static int buttons = 7;	/* all mouse buttons _up_ !! */

  /*
    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] = 0000 0000 ADB packet identifer.
    data[1] = ???? ???? (?)
    data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device.
    data[3] = bxxx xxxx First button and x-axis motion.
    data[4] = byyy yyyy Second button and y-axis motion.

    NOTE: data[0] is confirmed by the parent function and need not be
    checked here.
  */

  /*
    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] = 0000 0000 ADB packet identifer.
    data[1] = 0100 0000 Extended protocol register.
	      Bits 6-7 are the device id, which should be 1.
	      Bits 4-5 are resolution which is in "units/inch".
	      The Logitech MouseMan returns these bits clear but it has
	      200/300cpi resolution.
	      Bits 0-3 are unique vendor id.
    data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device.
	      Bits 2-3 should be 8 + 4.
		      Bits 4-7 should be 3 for a mouse device.
    data[3] = bxxx xxxx Left button and x-axis motion.
    data[4] = byyy yyyy Second button and y-axis motion.
    data[5] = byyy bxxx Third button and fourth button.  
    	      Y is additiona. high bits of y-axis motion.  
    	      X is additional high bits of x-axis motion.

    NOTE: data[0] and data[2] are confirmed by the parent function and
    need not be checked here.
  */

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

    /* x/y and buttons swapped */
    
    if (buf[0] ==  0)	{				/* real packet : use buttons? */
#ifdef DEBUG_ADBMOUSE
	if (console_loglevel >= 8)
	    printk("mac_mouse: real data; "); 
#endif
	/* button 1 (left, bit 2) : always significant ! */
	buttons = (buttons&3) | (buf[3] & 0x80 ? 4 : 0); /* 1+2 unchanged */
	/* button 2 (right, bit 0) present ? */
	if ( !mac_emulate_button2 ) 
	    buttons = (buttons&6) | (buf[4] & 0x80 ? 1 : 0); /* 2+3 unchanged */
	/* button 2 (middle) present? */
	/* data valid only if extended mouse format ! (buf[3] = 0 else)*/
	if ( !mac_emulate_button3 && buf[1]&0x40 )
	    buttons = (buttons&5) | (buf[5] & 0x80 ? 2 : 0); /* 1+3 unchanged */
    } else {					/* fake packet : use 2+3 */
#ifdef DEBUG_ADBMOUSE
	if (console_loglevel >= 8)
	    printk("mac_mouse: fake data; "); 
#endif
	/* we only see state changes here, but the fake driver takes care
	 * to preserve state... button 1 state must stay unchanged! */
	buttons = (buttons&4) | ((buf[4] & 0x80 ? 1 : 0) | (buf[5] & 0x80 ? 2 : 0));
    }

    add_mouse_randomness(((~buttons & 7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f));
    mouse.buttons = buttons & 7;
    mouse.dx += ((buf[4]&0x7f) < 64 ? (buf[4]&0x7f) : (buf[4]&0x7f)-128 );
    mouse.dy -= ((buf[3]&0x7f) < 64 ? (buf[3]&0x7f) : (buf[3]&0x7f)-128 );

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

}

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

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

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

    mac_mouse_interrupt_hook = NULL;
    MOD_DEC_USE_COUNT;
    return 0;
}

static int open_mouse(struct inode *inode, struct file *file)
{
    if (mouse.active++)
	return 0;
	
    mouse.ready = 0;

    mouse.dx = mouse.dy = 0;
    mac_mouse_buttons = 0;
    MOD_INC_USE_COUNT;
    mac_mouse_interrupt_hook = mac_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 mac_mouse_fops = {
    NULL,		/* mouse_seek */
    read_mouse,
    write_mouse,
    NULL,		/* mouse_readdir */
    mouse_poll,
    NULL,		/* mouse_ioctl */
    NULL,		/* mouse_mmap */
    open_mouse,
    release_mouse,
    NULL,
    fasync_mouse,
};

#define ADB_MOUSE_MINOR	10

static struct miscdevice mac_mouse = {
    ADB_MOUSE_MINOR, "adbmouse", &mac_mouse_fops
};

__initfunc(int mac_mouse_init(void))
{
    mouse.active = 0;
    mouse.ready = 0;
    mouse.wait = NULL;

    if (!MACH_IS_MAC)
	return -ENODEV;

    printk(KERN_INFO "Macintosh ADB mouse installed.\n");
    misc_register(&mac_mouse);
    return 0;
}


#define	MIN_THRESHOLD 1
#define	MAX_THRESHOLD 20	/* more seems not reasonable... */

__initfunc(void mac_mouse_setup(char *str, int *ints))
{
    if (ints[0] < 1) {
	printk( "mac_mouse_setup: no arguments!\n" );
	return;
    }
    else if (ints[0] > 2) {
	printk( "mac_mouse_setup: too many arguments\n" );
    }

    if (ints[1] < MIN_THRESHOLD || ints[1] > MAX_THRESHOLD)
	printk( "mac_mouse_setup: bad threshold value (ignored)\n" );
    else {
	mac_mouse_x_threshold = ints[1];
	mac_mouse_y_threshold = ints[1];
	if (ints[0] > 1) {
	    if (ints[2] < MIN_THRESHOLD || ints[2] > MAX_THRESHOLD)
		printk("mac_mouse_setup: bad threshold value (ignored)\n" );
	    else
		mac_mouse_y_threshold = ints[2];
	}
    }
	
}

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

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

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