Add additional config options to remove parts of code.
[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" // GET_EBDA
9 #include "util.h" // debug_isr
10 #include "pic.h" // unmask_pic2
11 #include "bregs.h" // struct bregs
12 #include "ps2port.h" // aux_command
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 #define RET_SUCCESS      0x00
27 #define RET_EINVFUNCTION 0x01
28 #define RET_EINVINPUT    0x02
29 #define RET_EINTERFACE   0x03
30 #define RET_ENEEDRESEND  0x04
31 #define RET_ENOHANDLER   0x05
32
33 static int
34 disable_mouse()
35 {
36     u8 ps2ctr = GET_EBDA(ps2ctr);
37     ps2ctr |= I8042_CTR_AUXDIS;
38     ps2ctr &= ~I8042_CTR_AUXINT;
39     SET_EBDA(ps2ctr, ps2ctr);
40
41     return aux_command(PSMOUSE_CMD_DISABLE, NULL);
42 }
43
44 // Disable Mouse
45 static void
46 mouse_15c20000(struct bregs *regs)
47 {
48     int ret = disable_mouse();
49     if (ret)
50         set_code_fail(regs, RET_ENEEDRESEND);
51     else
52         set_code_success(regs);
53 }
54
55 // Enable Mouse
56 static void
57 mouse_15c20001(struct bregs *regs)
58 {
59     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
60     if ((mouse_flags_2 & 0x80) == 0) {
61         set_code_fail(regs, RET_ENOHANDLER);
62         return;
63     }
64
65     u8 ps2ctr = GET_EBDA(ps2ctr);
66     ps2ctr &= ~I8042_CTR_AUXDIS;
67     ps2ctr |= I8042_CTR_AUXINT;
68     SET_EBDA(ps2ctr, ps2ctr);
69
70     int ret = aux_command(PSMOUSE_CMD_ENABLE, NULL);
71     if (ret)
72         set_code_fail(regs, RET_ENEEDRESEND);
73     else
74         set_code_success(regs);
75 }
76
77 static void
78 mouse_15c200XX(struct bregs *regs)
79 {
80     set_code_fail(regs, RET_EINVFUNCTION);
81 }
82
83 // Disable/Enable Mouse
84 static void
85 mouse_15c200(struct bregs *regs)
86 {
87     switch (regs->bh) {
88     case 0x00: mouse_15c20000(regs); break;
89     case 0x01: mouse_15c20001(regs); break;
90     default:   mouse_15c200XX(regs); break;
91     }
92 }
93
94 // Reset Mouse
95 static void
96 mouse_15c201(struct bregs *regs)
97 {
98     u8 param[2];
99     int ret = aux_command(PSMOUSE_CMD_RESET_BAT, param);
100     if (ret) {
101         set_code_fail(regs, RET_ENEEDRESEND);
102         return;
103     }
104     regs->bl = param[0];
105     regs->bh = param[1];
106     set_code_success(regs);
107 }
108
109 // Set Sample Rate
110 static void
111 mouse_15c202(struct bregs *regs)
112 {
113     static u8 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
114     if (regs->bh >= ARRAY_SIZE(sample_rates)) {
115         set_code_fail(regs, RET_EINVINPUT);
116         return;
117     }
118     u8 mouse_data1 = GET_VAR(CS, sample_rates[regs->bh]);
119     int ret = aux_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
120     if (ret)
121         set_code_fail(regs, RET_ENEEDRESEND);
122     else
123         set_code_success(regs);
124 }
125
126 // Set Resolution
127 static void
128 mouse_15c203(struct bregs *regs)
129 {
130     // BH:
131     //      0 =  25 dpi, 1 count  per millimeter
132     //      1 =  50 dpi, 2 counts per millimeter
133     //      2 = 100 dpi, 4 counts per millimeter
134     //      3 = 200 dpi, 8 counts per millimeter
135     if (regs->bh >= 4) {
136         set_code_fail(regs, RET_EINVINPUT);
137         return;
138     }
139     u8 param = regs->bh;
140     int ret = aux_command(PSMOUSE_CMD_SETRES, &param);
141     if (ret)
142         set_code_fail(regs, RET_ENEEDRESEND);
143     else
144         set_code_success(regs);
145 }
146
147 // Get Device ID
148 static void
149 mouse_15c204(struct bregs *regs)
150 {
151     u8 param[2];
152     int ret = aux_command(PSMOUSE_CMD_GETID, param);
153     if (ret) {
154         set_code_fail(regs, RET_ENEEDRESEND);
155         return;
156     }
157     regs->bh = param[0];
158     set_code_success(regs);
159 }
160
161 // Initialize Mouse
162 static void
163 mouse_15c205(struct bregs *regs)
164 {
165     if (regs->bh != 3) {
166         set_code_fail(regs, RET_EINTERFACE);
167         return;
168     }
169     SET_EBDA(mouse_flag1, 0x00);
170     SET_EBDA(mouse_flag2, regs->bh);
171
172     // Reset Mouse
173     mouse_15c201(regs);
174 }
175
176 // Return Status
177 static void
178 mouse_15c20600(struct bregs *regs)
179 {
180     u8 param[3];
181     int ret = aux_command(PSMOUSE_CMD_GETINFO, param);
182     if (ret) {
183         set_code_fail(regs, RET_ENEEDRESEND);
184         return;
185     }
186     regs->bl = param[0];
187     regs->cl = param[1];
188     regs->dl = param[2];
189     set_code_success(regs);
190 }
191
192 // Set Scaling Factor to 1:1
193 static void
194 mouse_15c20601(struct bregs *regs)
195 {
196     int ret = aux_command(PSMOUSE_CMD_SETSCALE11, NULL);
197     if (ret)
198         set_code_fail(regs, RET_ENEEDRESEND);
199     else
200         set_code_success(regs);
201 }
202
203 // Set Scaling Factor to 2:1
204 static void
205 mouse_15c20602(struct bregs *regs)
206 {
207     int ret = aux_command(PSMOUSE_CMD_SETSCALE21, NULL);
208     if (ret)
209         set_code_fail(regs, RET_ENEEDRESEND);
210     else
211         set_code_success(regs);
212 }
213
214 static void
215 mouse_15c206XX(struct bregs *regs)
216 {
217     set_code_fail(regs, RET_EINVFUNCTION);
218 }
219
220 // Return Status & Set Scaling Factor...
221 static void
222 mouse_15c206(struct bregs *regs)
223 {
224     switch (regs->bh) {
225     case 0x00: mouse_15c20600(regs); break;
226     case 0x01: mouse_15c20601(regs); break;
227     case 0x02: mouse_15c20602(regs); break;
228     default:   mouse_15c206XX(regs); break;
229     }
230 }
231
232 // Set Mouse Handler Address
233 static void
234 mouse_15c207(struct bregs *regs)
235 {
236     u32 farptr = (regs->es << 16) | regs->bx;
237     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
238     if (! farptr) {
239         /* remove handler */
240         if ((mouse_flags_2 & 0x80) != 0) {
241             mouse_flags_2 &= ~0x80;
242             disable_mouse();
243         }
244     } else {
245         /* install handler */
246         mouse_flags_2 |= 0x80;
247     }
248     SET_EBDA(mouse_flag2, mouse_flags_2);
249     SET_EBDA(far_call_pointer, farptr);
250     set_code_success(regs);
251 }
252
253 static void
254 mouse_15c2XX(struct bregs *regs)
255 {
256     set_code_fail(regs, RET_EINVFUNCTION);
257 }
258
259 void
260 handle_15c2(struct bregs *regs)
261 {
262     //debug_stub(regs);
263
264     if (! CONFIG_PS2_MOUSE) {
265         set_code_fail(regs, RET_EUNSUPPORTED);
266         return;
267     }
268
269     irq_enable();
270
271     switch (regs->al) {
272     case 0x00: mouse_15c200(regs); break;
273     case 0x01: mouse_15c201(regs); break;
274     case 0x02: mouse_15c202(regs); break;
275     case 0x03: mouse_15c203(regs); break;
276     case 0x04: mouse_15c204(regs); break;
277     case 0x05: mouse_15c205(regs); break;
278     case 0x06: mouse_15c206(regs); break;
279     case 0x07: mouse_15c207(regs); break;
280     default:   mouse_15c2XX(regs); break;
281     }
282 }
283
284 static void
285 int74_function()
286 {
287     u8 v = inb(PORT_PS2_STATUS);
288     if ((v & 0x21) != 0x21) {
289         dprintf(1, "int74 but no mouse data.\n");
290         return;
291     }
292     v = inb(PORT_PS2_DATA);
293
294     u8 mouse_flags_1 = GET_EBDA(mouse_flag1);
295     u8 mouse_flags_2 = GET_EBDA(mouse_flag2);
296
297     if (! (mouse_flags_2 & 0x80))
298         // far call handler not installed
299         return;
300
301     u8 package_count = mouse_flags_2 & 0x07;
302     u8 index = mouse_flags_1 & 0x07;
303     SET_EBDA(mouse_data[index], v);
304
305     if ((index+1) < package_count) {
306         mouse_flags_1++;
307         SET_EBDA(mouse_flag1, mouse_flags_1);
308         return;
309     }
310
311     //BX_DEBUG_INT74("int74_function: make_farcall=1\n");
312     u16 status = GET_EBDA(mouse_data[0]);
313     u16 X      = GET_EBDA(mouse_data[1]);
314     u16 Y      = GET_EBDA(mouse_data[2]);
315     SET_EBDA(mouse_flag1, 0);
316
317     u32 func = GET_EBDA(far_call_pointer);
318     asm volatile(
319         "pushl %0\n"
320         "pushw %w1\n"  // status
321         "pushw %w2\n"  // X
322         "pushw %w3\n"  // Y
323         "pushw $0\n"   // Z
324         "lcallw *8(%%esp)\n"
325         "addl $12, %%esp\n"
326         "cld\n"
327         : "+a" (func), "+b" (status), "+c" (X), "+d" (Y)
328         :
329         : "esi", "edi", "ebp", "cc"
330         );
331 }
332
333 // INT74h : PS/2 mouse hardware interrupt
334 void VISIBLE16
335 handle_74()
336 {
337     debug_isr(DEBUG_ISR_74);
338     if (! CONFIG_PS2_MOUSE)
339         goto done;
340
341     irq_enable();
342     int74_function();
343     irq_disable();
344
345 done:
346     eoi_pic2();
347 }