Don't save/restore flags and ebp on external calls - saves on stack space.
[seabios.git] / src / mouse.c
1 // 16bit code to handle mouse events.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "biosvar.h" // struct bregs
9 #include "util.h" // debug_enter
10
11 #define DEBUGF1(fmt, args...) bprintf(0, fmt , ##args)
12 #define DEBUGF(fmt, args...)
13
14 static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
15
16 static void
17 set_kbd_command_byte(u8 command_byte)
18 {
19     if (inb(PORT_PS2_STATUS) & 0x02)
20         BX_PANIC(panic_msg_keyb_buffer_full, "setkbdcomm");
21     outb(0xD4, PORT_PS2_STATUS);
22
23     outb(0x60, PORT_PS2_STATUS); // write command byte
24     outb(command_byte, PORT_PS2_DATA);
25 }
26
27 static u8
28 inhibit_mouse_int_and_events()
29 {
30     // Turn off IRQ generation and aux data line
31     if (inb(PORT_PS2_STATUS) & 0x02)
32         BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
33     outb(0x20, PORT_PS2_STATUS); // get command byte
34     while ((inb(PORT_PS2_STATUS) & 0x01) != 0x01)
35         ;
36     u8 prev_command_byte = inb(PORT_PS2_DATA);
37     u8 command_byte = prev_command_byte;
38     //while ( (inb(PORT_PS2_STATUS) & 0x02) );
39     if ( inb(PORT_PS2_STATUS) & 0x02 )
40         BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
41     command_byte &= 0xfd; // turn off IRQ 12 generation
42     command_byte |= 0x20; // disable mouse serial clock line
43     outb(0x60, PORT_PS2_STATUS); // write command byte
44     outb(command_byte, PORT_PS2_DATA);
45     return prev_command_byte;
46 }
47
48 static void
49 enable_mouse_int_and_events()
50 {
51     // Turn on IRQ generation and aux data line
52     if ( inb(PORT_PS2_STATUS) & 0x02 )
53         BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
54     outb(0x20, PORT_PS2_STATUS); // get command byte
55     while ((inb(PORT_PS2_STATUS) & 0x01) != 0x01)
56         ;
57     u8 command_byte = inb(PORT_PS2_DATA);
58     //while ( (inb(PORT_PS2_STATUS) & 0x02) );
59     if (inb(PORT_PS2_STATUS) & 0x02)
60         BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
61     command_byte |= 0x02; // turn on IRQ 12 generation
62     command_byte &= 0xdf; // enable mouse serial clock line
63     outb(0x60, PORT_PS2_STATUS); // write command byte
64     outb(command_byte, PORT_PS2_DATA);
65 }
66
67 static void
68 send_to_mouse_ctrl(u8 sendbyte)
69 {
70     // wait for chance to write to ctrl
71     if (inb(PORT_PS2_STATUS) & 0x02)
72         BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
73     outb(0xD4, PORT_PS2_STATUS);
74     outb(sendbyte, PORT_PS2_DATA);
75 }
76
77 static void
78 get_mouse_data(u8 *data)
79 {
80     while ((inb(PORT_PS2_STATUS) & 0x21) != 0x21)
81         ;
82     *data = inb(PORT_PS2_DATA);
83 }
84
85 #define RET_SUCCESS      0x00
86 #define RET_EINVFUNCTION 0x01
87 #define RET_EINVINPUT    0x02
88 #define RET_EINTERFACE   0x03
89 #define RET_ENEEDRESEND  0x04
90 #define RET_ENOHANDLER   0x05
91
92 // Disable Mouse
93 static void
94 mouse_15c20000(struct bregs *regs)
95 {
96     inhibit_mouse_int_and_events(); // disable IRQ12 and packets
97     send_to_mouse_ctrl(0xF5); // disable mouse command
98     u8 mouse_data1;
99     get_mouse_data(&mouse_data1);
100     set_code_success(regs);
101 }
102
103 #define BX_DEBUG_INT15(args...)
104
105 // Enable Mouse
106 static void
107 mouse_15c20001(struct bregs *regs)
108 {
109     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
110     if ((mouse_flags_2 & 0x80) == 0) {
111         BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
112         set_code_fail(regs, RET_ENOHANDLER);
113         return;
114     }
115     inhibit_mouse_int_and_events(); // disable IRQ12 and packets
116     send_to_mouse_ctrl(0xF4); // enable mouse command
117     u8 mouse_data1;
118     get_mouse_data(&mouse_data1);
119     if (mouse_data1 == 0xFA) {
120         enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
121         set_code_success(regs);
122         return;
123     }
124     set_code_fail(regs, RET_ENEEDRESEND);
125 }
126
127 static void
128 mouse_15c200XX(struct bregs *regs)
129 {
130     set_code_fail(regs, RET_EINVFUNCTION);
131 }
132
133 // Disable/Enable Mouse
134 static void
135 mouse_15c200(struct bregs *regs)
136 {
137     switch (regs->bh) {
138     case 0x00: mouse_15c20000(regs); break;
139     case 0x01: mouse_15c20001(regs); break;
140     default:   mouse_15c200XX(regs); break;
141     }
142 }
143
144 // Reset Mouse
145 static void
146 mouse_15c201(struct bregs *regs)
147 {
148     inhibit_mouse_int_and_events(); // disable IRQ12 and packets
149     send_to_mouse_ctrl(0xFF); // reset mouse command
150     u8 mouse_data1, mouse_data2, mouse_data3;
151     get_mouse_data(&mouse_data3);
152     // if no mouse attached, it will return RESEND
153     if (mouse_data3 == 0xfe) {
154         set_code_fail(regs, RET_ENEEDRESEND);
155         return;
156     }
157     if (mouse_data3 != 0xfa)
158         BX_PANIC("Mouse reset returned %02x (should be ack)\n"
159                  , (unsigned)mouse_data3);
160     get_mouse_data(&mouse_data1);
161     get_mouse_data(&mouse_data2);
162     // turn IRQ12 and packet generation on
163     enable_mouse_int_and_events();
164     regs->bl = mouse_data1;
165     regs->bh = mouse_data2;
166     set_code_success(regs);
167 }
168
169 // Set Sample Rate
170 static void
171 mouse_15c202(struct bregs *regs)
172 {
173     if (regs->bh >= 7) {
174         set_code_fail(regs, RET_EUNSUPPORTED);
175         return;
176     }
177     u8 mouse_data1 = regs->bh * 20;
178     if (!mouse_data1)
179         mouse_data1 = 10;
180     send_to_mouse_ctrl(0xF3); // set sample rate command
181     u8 mouse_data2;
182     get_mouse_data(&mouse_data2);
183     send_to_mouse_ctrl(mouse_data1);
184     get_mouse_data(&mouse_data2);
185     set_code_success(regs);
186 }
187
188 // Set Resolution
189 static void
190 mouse_15c203(struct bregs *regs)
191 {
192     // BH:
193     //      0 =  25 dpi, 1 count  per millimeter
194     //      1 =  50 dpi, 2 counts per millimeter
195     //      2 = 100 dpi, 4 counts per millimeter
196     //      3 = 200 dpi, 8 counts per millimeter
197     u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
198     if (regs->bh >= 4) {
199         set_code_fail(regs, RET_EUNSUPPORTED);
200         goto done;
201     }
202     send_to_mouse_ctrl(0xE8); // set resolution command
203     u8 mouse_data1;
204     get_mouse_data(&mouse_data1);
205     if (mouse_data1 != 0xfa)
206         BX_PANIC("Mouse status returned %02x (should be ack)\n"
207                  , (unsigned)mouse_data1);
208     send_to_mouse_ctrl(regs->bh);
209     get_mouse_data(&mouse_data1);
210     if (mouse_data1 != 0xfa)
211         BX_PANIC("Mouse status returned %02x (should be ack)\n"
212                  , (unsigned)mouse_data1);
213     set_code_success(regs);
214
215 done:
216     set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
217 }
218
219 // Get Device ID
220 static void
221 mouse_15c204(struct bregs *regs)
222 {
223     inhibit_mouse_int_and_events(); // disable IRQ12 and packets
224     send_to_mouse_ctrl(0xF2); // get mouse ID command
225     u8 mouse_data1, mouse_data2;
226     get_mouse_data(&mouse_data1);
227     get_mouse_data(&mouse_data2);
228     regs->bh = mouse_data2;
229     set_code_success(regs);
230 }
231
232 // Initialize Mouse
233 static void
234 mouse_15c205(struct bregs *regs)
235 {
236     if (regs->bh != 3) {
237         set_code_fail(regs, RET_EINTERFACE);
238         return;
239     }
240     SET_EBDA(mouse_flag1, 0x00);
241     SET_EBDA(mouse_flag2, regs->bh);
242
243     // Reset Mouse
244     mouse_15c201(regs);
245 }
246
247 // Return Status
248 static void
249 mouse_15c20600(struct bregs *regs)
250 {
251     u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
252     send_to_mouse_ctrl(0xE9); // get mouse info command
253     u8 mouse_data1, mouse_data2, mouse_data3;
254     get_mouse_data(&mouse_data1);
255     if (mouse_data1 != 0xfa)
256         BX_PANIC("Mouse status returned %02x (should be ack)\n"
257                  , (unsigned)mouse_data1);
258     get_mouse_data(&mouse_data1);
259     get_mouse_data(&mouse_data2);
260     get_mouse_data(&mouse_data3);
261     regs->bl = mouse_data1;
262     regs->cl = mouse_data2;
263     regs->dl = mouse_data3;
264     set_code_success(regs);
265     set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
266 }
267
268 static void
269 set_scaling(struct bregs *regs, u8 cmd)
270 {
271     u8 comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
272     send_to_mouse_ctrl(0xE6);
273     u8 mouse_data1;
274     get_mouse_data(&mouse_data1);
275     if (mouse_data1 != 0xFA)
276         set_code_fail(regs, RET_EUNSUPPORTED);
277     else
278         set_code_success(regs);
279     set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
280 }
281
282 // Set Scaling Factor to 1:1
283 static void
284 mouse_15c20601(struct bregs *regs)
285 {
286     set_scaling(regs, 0xE6);
287 }
288
289 // Set Scaling Factor to 2:1
290 static void
291 mouse_15c20602(struct bregs *regs)
292 {
293     set_scaling(regs, 0xE7);
294 }
295
296 static void
297 mouse_15c206XX(struct bregs *regs)
298 {
299     BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", regs->bh);
300 }
301
302 // Return Status & Set Scaling Factor...
303 static void
304 mouse_15c206(struct bregs *regs)
305 {
306     switch (regs->bh) {
307     case 0x00: mouse_15c20600(regs); break;
308     case 0x01: mouse_15c20601(regs); break;
309     case 0x02: mouse_15c20602(regs); break;
310     default:   mouse_15c206XX(regs); break;
311     }
312 }
313
314 // Set Mouse Handler Address
315 static void
316 mouse_15c207(struct bregs *regs)
317 {
318     u32 farptr = (regs->es << 16) | regs->bx;
319     SET_EBDA(far_call_pointer, farptr);
320     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
321     if (! farptr) {
322         /* remove handler */
323         if ((mouse_flags_2 & 0x80) != 0) {
324             mouse_flags_2 &= ~0x80;
325             inhibit_mouse_int_and_events(); // disable IRQ12 and packets
326         }
327     } else {
328         /* install handler */
329         mouse_flags_2 |= 0x80;
330     }
331     SET_EBDA(mouse_flag2, mouse_flags_2);
332     set_code_success(regs);
333 }
334
335 static void
336 mouse_15c2XX(struct bregs *regs)
337 {
338     set_code_fail(regs, RET_EINVFUNCTION);
339 }
340
341 void
342 handle_15c2(struct bregs *regs)
343 {
344     //debug_stub(regs);
345
346     if (! CONFIG_PS2_MOUSE) {
347         set_code_fail(regs, RET_EUNSUPPORTED);
348         return;
349     }
350
351     switch (regs->al) {
352     case 0x00: mouse_15c200(regs); break;
353     case 0x01: mouse_15c201(regs); break;
354     case 0x02: mouse_15c202(regs); break;
355     case 0x03: mouse_15c203(regs); break;
356     case 0x04: mouse_15c204(regs); break;
357     case 0x05: mouse_15c205(regs); break;
358     case 0x06: mouse_15c206(regs); break;
359     case 0x07: mouse_15c207(regs); break;
360     default:   mouse_15c2XX(regs); break;
361     }
362 }
363
364 static void
365 int74_function()
366 {
367     u8 v = inb(PORT_PS2_STATUS);
368     if ((v & 0x21) != 0x21)
369         return;
370
371     v = inb(PORT_PS2_DATA);
372
373     u8 mouse_flags_1 = GET_EBDA(mouse_flag1);
374     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
375
376     if ((mouse_flags_2 & 0x80) != 0x80)
377         return;
378
379     u8 package_count = mouse_flags_2 & 0x07;
380     u8 index = mouse_flags_1 & 0x07;
381     SET_EBDA(mouse_data[index], v);
382
383     if ((index+1) < package_count) {
384         mouse_flags_1++;
385         SET_EBDA(mouse_flag1, mouse_flags_1);
386         return;
387     }
388
389     //BX_DEBUG_INT74("int74_function: make_farcall=1\n");
390     u16 status = GET_EBDA(mouse_data[0]);
391     u16 X      = GET_EBDA(mouse_data[1]);
392     u16 Y      = GET_EBDA(mouse_data[2]);
393     SET_EBDA(mouse_flag1, 0);
394     // check if far call handler installed
395     if (! (mouse_flags_2 & 0x80))
396         return;
397
398     u32 func = GET_EBDA(far_call_pointer);
399     asm volatile(
400         "pushl %0\n"
401         "pushw %w1\n"  // status
402         "pushw %w2\n"  // X
403         "pushw %w3\n"  // Y
404         "pushw $0\n"   // Z
405         "lcallw *8(%%esp)\n"
406         "addl $12, %%esp\n"
407         "cld\n"
408         : "+a" (func), "+b" (status), "+c" (X), "+d" (Y)
409         :
410         : "esi", "edi", "ebp", "cc"
411         );
412 }
413
414 // INT74h : PS/2 mouse hardware interrupt
415 void VISIBLE16
416 handle_74()
417 {
418     //debug_isr();
419
420     irq_enable();
421     int74_function();
422     irq_disable();
423
424     eoi_both_pics();
425 }