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