blob: b7ad7c62a09beffb12495803eeec234533f29bea [file] [log] [blame]
Kevin O'Connor4b60c002008-02-25 22:29:55 -05001// 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//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor4b60c002008-02-25 22:29:55 -05007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // GET_EBDA
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "bregs.h" // struct bregs
Kevin O'Connor5d369d82013-09-02 20:48:46 -040010#include "hw/ps2port.h" // ps2_mouse_command
11#include "hw/usb-hid.h" // usb_mouse_command
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "output.h" // dprintf
Kevin O'Connor12085432014-09-30 09:17:14 -040013#include "stacks.h" // stack_hop_back
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "util.h" // mouse_init
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050015
Kevin O'Connorf54c1502008-06-14 15:56:16 -040016void
Kevin O'Connord83c87b2013-01-21 01:14:12 -050017mouse_init(void)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040018{
Kevin O'Connor57877482009-12-09 21:00:41 -050019 if (! CONFIG_MOUSE)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040020 return;
21 dprintf(3, "init mouse\n");
22 // pointing device installed
Kevin O'Connore51316d2012-06-10 09:09:22 -040023 set_equipment_flags(0x04, 0x04);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040024}
25
Kevin O'Connord488a762012-05-28 14:34:49 -040026static int
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040027mouse_command(int command, u8 *param)
28{
Kevin O'Connor0e885762010-05-01 22:14:40 -040029 if (usb_mouse_active())
Kevin O'Connor12085432014-09-30 09:17:14 -040030 return usb_mouse_command(command, param);
31 return ps2_mouse_command(command, param);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040032}
33
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050034#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
42static void
43mouse_15c20000(struct bregs *regs)
44{
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -040045 int ret = mouse_command(PSMOUSE_CMD_DISABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040046 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050047 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040048 else
49 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050050}
51
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050052// Enable Mouse
53static void
54mouse_15c20001(struct bregs *regs)
55{
Kevin O'Connor4bc49972012-05-13 22:58:08 -040056 u16 ebda_seg = get_ebda_seg();
57 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050058 if ((mouse_flags_2 & 0x80) == 0) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050059 set_code_invalid(regs, RET_ENOHANDLER);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050060 return;
61 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040062
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040063 int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040064 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050065 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040066 else
Kevin O'Connor6c781222008-03-09 12:19:23 -040067 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050068}
69
70static void
71mouse_15c200XX(struct bregs *regs)
72{
Kevin O'Connordfefeb52009-12-13 13:04:17 -050073 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050074}
75
76// Disable/Enable Mouse
77static void
78mouse_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
88static void
89mouse_15c201(struct bregs *regs)
90{
Kevin O'Connor3b897192008-07-20 10:08:59 -040091 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040092 int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param);
Kevin O'Connorb44a8522009-01-17 23:30:01 -050093 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050094 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050095 return;
96 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040097 regs->bl = param[0];
98 regs->bh = param[1];
Kevin O'Connor6c781222008-03-09 12:19:23 -040099 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500100}
101
102// Set Sample Rate
103static void
104mouse_15c202(struct bregs *regs)
105{
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500106 static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
Kevin O'Connor3b897192008-07-20 10:08:59 -0400107 if (regs->bh >= ARRAY_SIZE(sample_rates)) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500108 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500109 return;
110 }
Kevin O'Connor15157a32008-12-13 11:10:37 -0500111 u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400112 int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400113 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500114 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400115 else
116 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500117}
118
119// Set Resolution
120static void
121mouse_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
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500128 if (regs->bh >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500129 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400130 return;
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500131 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400132 u8 param = regs->bh;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400133 int ret = mouse_command(PSMOUSE_CMD_SETRES, &param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400134 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500135 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400136 else
137 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500138}
139
140// Get Device ID
141static void
142mouse_15c204(struct bregs *regs)
143{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400144 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400145 int ret = mouse_command(PSMOUSE_CMD_GETID, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400146 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500147 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400148 return;
149 }
150 regs->bh = param[0];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400151 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500152}
153
154// Initialize Mouse
155static void
156mouse_15c205(struct bregs *regs)
157{
158 if (regs->bh != 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500159 set_code_invalid(regs, RET_EINTERFACE);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500160 return;
161 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500162 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400163 SET_EBDA(ebda_seg, mouse_flag1, 0x00);
164 SET_EBDA(ebda_seg, mouse_flag2, regs->bh);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500165
166 // Reset Mouse
167 mouse_15c201(regs);
168}
169
170// Return Status
171static void
172mouse_15c20600(struct bregs *regs)
173{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400174 u8 param[3];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400175 int ret = mouse_command(PSMOUSE_CMD_GETINFO, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400176 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500177 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400178 return;
179 }
180 regs->bl = param[0];
181 regs->cl = param[1];
182 regs->dl = param[2];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400183 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500184}
185
186// Set Scaling Factor to 1:1
187static void
188mouse_15c20601(struct bregs *regs)
189{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400190 int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400191 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500192 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400193 else
194 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500195}
196
197// Set Scaling Factor to 2:1
198static void
199mouse_15c20602(struct bregs *regs)
200{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400201 int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400202 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500203 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400204 else
205 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500206}
207
208static void
209mouse_15c206XX(struct bregs *regs)
210{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500211 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500212}
213
214// Return Status & Set Scaling Factor...
215static void
216mouse_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
227static void
228mouse_15c207(struct bregs *regs)
229{
Kevin O'Connor9f985422009-09-09 11:34:39 -0400230 struct segoff_s farptr = SEGOFF(regs->es, regs->bx);
Kevin O'Connor08815372008-12-29 21:16:31 -0500231 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400232 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor9f985422009-09-09 11:34:39 -0400233 if (! farptr.segoff) {
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500234 /* remove handler */
235 if ((mouse_flags_2 & 0x80) != 0) {
236 mouse_flags_2 &= ~0x80;
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400237 mouse_command(PSMOUSE_CMD_DISABLE, NULL);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500238 }
239 } else {
240 /* install handler */
241 mouse_flags_2 |= 0x80;
242 }
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400243 SET_EBDA(ebda_seg, mouse_flag2, mouse_flags_2);
244 SET_EBDA(ebda_seg, far_call_pointer, farptr);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400245 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500246}
247
248static void
249mouse_15c2XX(struct bregs *regs)
250{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500251 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500252}
253
254void
255handle_15c2(struct bregs *regs)
256{
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500257 //debug_stub(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500258
Kevin O'Connor57877482009-12-09 21:00:41 -0500259 if (! CONFIG_MOUSE) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500260 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500261 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
Kevin O'Connor80568252014-09-29 19:39:31 -0400277void VISIBLE16
Kevin O'Connor7e1baf22014-09-29 19:23:45 -0400278invoke_mouse_handler(void)
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400279{
Kevin O'Connor80568252014-09-29 19:39:31 -0400280 if (!CONFIG_MOUSE)
281 return;
Kevin O'Connor7e1baf22014-09-29 19:23:45 -0400282 if (need_hop_back()) {
Kevin O'Connorb4cca862015-10-09 11:53:02 -0400283 stack_hop_back(invoke_mouse_handler, 0, 0);
Kevin O'Connor7e1baf22014-09-29 19:23:45 -0400284 return;
285 }
Kevin O'Connor80568252014-09-29 19:39:31 -0400286 ASSERT16();
Kevin O'Connor7e1baf22014-09-29 19:23:45 -0400287 u16 ebda_seg = get_ebda_seg();
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400288 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
Kevin O'Connorac7eb5e2012-05-28 14:42:16 -0400316void
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500317process_mouse(u8 data)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500318{
Kevin O'Connor57877482009-12-09 21:00:41 -0500319 if (!CONFIG_MOUSE)
320 return;
321
Kevin O'Connor08815372008-12-29 21:16:31 -0500322 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400323 u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1);
324 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500325
Kevin O'Connor65e63422008-07-19 14:12:32 -0400326 if (! (mouse_flags_2 & 0x80))
327 // far call handler not installed
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500328 return;
329
330 u8 package_count = mouse_flags_2 & 0x07;
331 u8 index = mouse_flags_1 & 0x07;
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400332 SET_EBDA(ebda_seg, mouse_data[index], data);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500333
334 if ((index+1) < package_count) {
335 mouse_flags_1++;
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400336 SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500337 return;
338 }
339
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400340 SET_EBDA(ebda_seg, mouse_flag1, 0);
Kevin O'Connor7e1baf22014-09-29 19:23:45 -0400341 invoke_mouse_handler();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500342}