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