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