Change license from GPLv3 to LGPLv3.
[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 LGPLv3 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(u16 ebda_seg)
34 {
35     u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
36     ps2ctr |= I8042_CTR_AUXDIS;
37     ps2ctr &= ~I8042_CTR_AUXINT;
38     SET_EBDA2(ebda_seg, 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     u16 ebda_seg = get_ebda_seg();
48     int ret = disable_mouse(ebda_seg);
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     u16 ebda_seg = get_ebda_seg();
60     u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
61     if ((mouse_flags_2 & 0x80) == 0) {
62         set_code_fail(regs, RET_ENOHANDLER);
63         return;
64     }
65
66     u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
67     ps2ctr &= ~I8042_CTR_AUXDIS;
68     ps2ctr |= I8042_CTR_AUXINT;
69     SET_EBDA2(ebda_seg, ps2ctr, ps2ctr);
70
71     int ret = aux_command(PSMOUSE_CMD_ENABLE, NULL);
72     if (ret)
73         set_code_fail(regs, RET_ENEEDRESEND);
74     else
75         set_code_success(regs);
76 }
77
78 static void
79 mouse_15c200XX(struct bregs *regs)
80 {
81     set_code_fail(regs, RET_EINVFUNCTION);
82 }
83
84 // Disable/Enable Mouse
85 static void
86 mouse_15c200(struct bregs *regs)
87 {
88     switch (regs->bh) {
89     case 0x00: mouse_15c20000(regs); break;
90     case 0x01: mouse_15c20001(regs); break;
91     default:   mouse_15c200XX(regs); break;
92     }
93 }
94
95 // Reset Mouse
96 static void
97 mouse_15c201(struct bregs *regs)
98 {
99     u8 param[2];
100     int ret = aux_command(PSMOUSE_CMD_RESET_BAT, param);
101     if (ret != 0 && ret != 2) {
102         set_code_fail(regs, RET_ENEEDRESEND);
103         return;
104     }
105     regs->bl = param[0];
106     regs->bh = param[1];
107     set_code_success(regs);
108 }
109
110 // Set Sample Rate
111 static void
112 mouse_15c202(struct bregs *regs)
113 {
114     static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
115     if (regs->bh >= ARRAY_SIZE(sample_rates)) {
116         set_code_fail(regs, RET_EINVINPUT);
117         return;
118     }
119     u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
120     int ret = aux_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
121     if (ret)
122         set_code_fail(regs, RET_ENEEDRESEND);
123     else
124         set_code_success(regs);
125 }
126
127 // Set Resolution
128 static void
129 mouse_15c203(struct bregs *regs)
130 {
131     // BH:
132     //      0 =  25 dpi, 1 count  per millimeter
133     //      1 =  50 dpi, 2 counts per millimeter
134     //      2 = 100 dpi, 4 counts per millimeter
135     //      3 = 200 dpi, 8 counts per millimeter
136     if (regs->bh >= 4) {
137         set_code_fail(regs, RET_EINVINPUT);
138         return;
139     }
140     u8 param = regs->bh;
141     int ret = aux_command(PSMOUSE_CMD_SETRES, &param);
142     if (ret)
143         set_code_fail(regs, RET_ENEEDRESEND);
144     else
145         set_code_success(regs);
146 }
147
148 // Get Device ID
149 static void
150 mouse_15c204(struct bregs *regs)
151 {
152     u8 param[2];
153     int ret = aux_command(PSMOUSE_CMD_GETID, param);
154     if (ret) {
155         set_code_fail(regs, RET_ENEEDRESEND);
156         return;
157     }
158     regs->bh = param[0];
159     set_code_success(regs);
160 }
161
162 // Initialize Mouse
163 static void
164 mouse_15c205(struct bregs *regs)
165 {
166     if (regs->bh != 3) {
167         set_code_fail(regs, RET_EINTERFACE);
168         return;
169     }
170     u16 ebda_seg = get_ebda_seg();
171     SET_EBDA2(ebda_seg, mouse_flag1, 0x00);
172     SET_EBDA2(ebda_seg, mouse_flag2, regs->bh);
173
174     // Reset Mouse
175     mouse_15c201(regs);
176 }
177
178 // Return Status
179 static void
180 mouse_15c20600(struct bregs *regs)
181 {
182     u8 param[3];
183     int ret = aux_command(PSMOUSE_CMD_GETINFO, param);
184     if (ret) {
185         set_code_fail(regs, RET_ENEEDRESEND);
186         return;
187     }
188     regs->bl = param[0];
189     regs->cl = param[1];
190     regs->dl = param[2];
191     set_code_success(regs);
192 }
193
194 // Set Scaling Factor to 1:1
195 static void
196 mouse_15c20601(struct bregs *regs)
197 {
198     int ret = aux_command(PSMOUSE_CMD_SETSCALE11, NULL);
199     if (ret)
200         set_code_fail(regs, RET_ENEEDRESEND);
201     else
202         set_code_success(regs);
203 }
204
205 // Set Scaling Factor to 2:1
206 static void
207 mouse_15c20602(struct bregs *regs)
208 {
209     int ret = aux_command(PSMOUSE_CMD_SETSCALE21, NULL);
210     if (ret)
211         set_code_fail(regs, RET_ENEEDRESEND);
212     else
213         set_code_success(regs);
214 }
215
216 static void
217 mouse_15c206XX(struct bregs *regs)
218 {
219     set_code_fail(regs, RET_EINVFUNCTION);
220 }
221
222 // Return Status & Set Scaling Factor...
223 static void
224 mouse_15c206(struct bregs *regs)
225 {
226     switch (regs->bh) {
227     case 0x00: mouse_15c20600(regs); break;
228     case 0x01: mouse_15c20601(regs); break;
229     case 0x02: mouse_15c20602(regs); break;
230     default:   mouse_15c206XX(regs); break;
231     }
232 }
233
234 // Set Mouse Handler Address
235 static void
236 mouse_15c207(struct bregs *regs)
237 {
238     u32 farptr = (regs->es << 16) | regs->bx;
239     u16 ebda_seg = get_ebda_seg();
240     u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
241     if (! farptr) {
242         /* remove handler */
243         if ((mouse_flags_2 & 0x80) != 0) {
244             mouse_flags_2 &= ~0x80;
245             disable_mouse(ebda_seg);
246         }
247     } else {
248         /* install handler */
249         mouse_flags_2 |= 0x80;
250     }
251     SET_EBDA2(ebda_seg, mouse_flag2, mouse_flags_2);
252     SET_EBDA2(ebda_seg, far_call_pointer, farptr);
253     set_code_success(regs);
254 }
255
256 static void
257 mouse_15c2XX(struct bregs *regs)
258 {
259     set_code_fail(regs, RET_EINVFUNCTION);
260 }
261
262 void
263 handle_15c2(struct bregs *regs)
264 {
265     //debug_stub(regs);
266
267     if (! CONFIG_PS2_MOUSE) {
268         set_code_fail(regs, RET_EUNSUPPORTED);
269         return;
270     }
271
272     irq_enable();
273
274     switch (regs->al) {
275     case 0x00: mouse_15c200(regs); break;
276     case 0x01: mouse_15c201(regs); break;
277     case 0x02: mouse_15c202(regs); break;
278     case 0x03: mouse_15c203(regs); break;
279     case 0x04: mouse_15c204(regs); break;
280     case 0x05: mouse_15c205(regs); break;
281     case 0x06: mouse_15c206(regs); break;
282     case 0x07: mouse_15c207(regs); break;
283     default:   mouse_15c2XX(regs); break;
284     }
285 }
286
287 static void
288 process_mouse(u8 data)
289 {
290     u16 ebda_seg = get_ebda_seg();
291     u8 mouse_flags_1 = GET_EBDA2(ebda_seg, mouse_flag1);
292     u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
293
294     if (! (mouse_flags_2 & 0x80))
295         // far call handler not installed
296         return;
297
298     u8 package_count = mouse_flags_2 & 0x07;
299     u8 index = mouse_flags_1 & 0x07;
300     SET_EBDA2(ebda_seg, mouse_data[index], data);
301
302     if ((index+1) < package_count) {
303         mouse_flags_1++;
304         SET_EBDA2(ebda_seg, mouse_flag1, mouse_flags_1);
305         return;
306     }
307
308     //BX_DEBUG_INT74("int74_function: make_farcall=1\n");
309     u16 status = GET_EBDA2(ebda_seg, mouse_data[0]);
310     u16 X      = GET_EBDA2(ebda_seg, mouse_data[1]);
311     u16 Y      = GET_EBDA2(ebda_seg, mouse_data[2]);
312     SET_EBDA2(ebda_seg, mouse_flag1, 0);
313
314     u32 func = GET_EBDA2(ebda_seg, far_call_pointer);
315
316     irq_enable();
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         :
327         : "r"(func), "r"(status), "r"(X), "r"(Y)
328         : "cc"
329         );
330     irq_disable();
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     u8 v = inb(PORT_PS2_STATUS);
342     if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
343         != (I8042_STR_OBF|I8042_STR_AUXDATA)) {
344         dprintf(1, "mouse irq but no mouse data.\n");
345         goto done;
346     }
347     v = inb(PORT_PS2_DATA);
348
349     process_mouse(v);
350
351 done:
352     eoi_pic2();
353 }