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