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