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