blob: 7b28a63247e2c2c1e7f1516abf8bf068070b03c6 [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'Connorecdc6552012-05-28 14:25:15 -04009#include "util.h" // dprintf
Kevin O'Connor9521e262008-07-04 13:04:29 -040010#include "bregs.h" // struct bregs
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040011#include "ps2port.h" // ps2_mouse_command
Kevin O'Connor0e885762010-05-01 22:14:40 -040012#include "usb-hid.h" // usb_mouse_command
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050013
Kevin O'Connorf54c1502008-06-14 15:56:16 -040014void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050015mouse_setup(void)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040016{
Kevin O'Connor57877482009-12-09 21:00:41 -050017 if (! CONFIG_MOUSE)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040018 return;
19 dprintf(3, "init mouse\n");
20 // pointing device installed
Kevin O'Connore51316d2012-06-10 09:09:22 -040021 set_equipment_flags(0x04, 0x04);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040022}
23
Kevin O'Connord488a762012-05-28 14:34:49 -040024static int
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040025mouse_command(int command, u8 *param)
26{
Kevin O'Connor0e885762010-05-01 22:14:40 -040027 if (usb_mouse_active())
Kevin O'Connord488a762012-05-28 14:34:49 -040028 return stack_hop(command, (u32)param, usb_mouse_command);
29 return stack_hop(command, (u32)param, ps2_mouse_command);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040030}
31
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050032#define RET_SUCCESS 0x00
33#define RET_EINVFUNCTION 0x01
34#define RET_EINVINPUT 0x02
35#define RET_EINTERFACE 0x03
36#define RET_ENEEDRESEND 0x04
37#define RET_ENOHANDLER 0x05
38
39// Disable Mouse
40static void
41mouse_15c20000(struct bregs *regs)
42{
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -040043 int ret = mouse_command(PSMOUSE_CMD_DISABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040044 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050045 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040046 else
47 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050048}
49
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050050// Enable Mouse
51static void
52mouse_15c20001(struct bregs *regs)
53{
Kevin O'Connor4bc49972012-05-13 22:58:08 -040054 u16 ebda_seg = get_ebda_seg();
55 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050056 if ((mouse_flags_2 & 0x80) == 0) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050057 set_code_invalid(regs, RET_ENOHANDLER);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050058 return;
59 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040060
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040061 int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040062 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050063 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040064 else
Kevin O'Connor6c781222008-03-09 12:19:23 -040065 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050066}
67
68static void
69mouse_15c200XX(struct bregs *regs)
70{
Kevin O'Connordfefeb52009-12-13 13:04:17 -050071 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050072}
73
74// Disable/Enable Mouse
75static void
76mouse_15c200(struct bregs *regs)
77{
78 switch (regs->bh) {
79 case 0x00: mouse_15c20000(regs); break;
80 case 0x01: mouse_15c20001(regs); break;
81 default: mouse_15c200XX(regs); break;
82 }
83}
84
85// Reset Mouse
86static void
87mouse_15c201(struct bregs *regs)
88{
Kevin O'Connor3b897192008-07-20 10:08:59 -040089 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040090 int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param);
Kevin O'Connorb44a8522009-01-17 23:30:01 -050091 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050092 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050093 return;
94 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040095 regs->bl = param[0];
96 regs->bh = param[1];
Kevin O'Connor6c781222008-03-09 12:19:23 -040097 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050098}
99
100// Set Sample Rate
101static void
102mouse_15c202(struct bregs *regs)
103{
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500104 static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
Kevin O'Connor3b897192008-07-20 10:08:59 -0400105 if (regs->bh >= ARRAY_SIZE(sample_rates)) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500106 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500107 return;
108 }
Kevin O'Connor15157a32008-12-13 11:10:37 -0500109 u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400110 int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400111 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500112 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400113 else
114 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500115}
116
117// Set Resolution
118static void
119mouse_15c203(struct bregs *regs)
120{
121 // BH:
122 // 0 = 25 dpi, 1 count per millimeter
123 // 1 = 50 dpi, 2 counts per millimeter
124 // 2 = 100 dpi, 4 counts per millimeter
125 // 3 = 200 dpi, 8 counts per millimeter
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500126 if (regs->bh >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500127 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400128 return;
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500129 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400130 u8 param = regs->bh;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400131 int ret = mouse_command(PSMOUSE_CMD_SETRES, &param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400132 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500133 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400134 else
135 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500136}
137
138// Get Device ID
139static void
140mouse_15c204(struct bregs *regs)
141{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400142 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400143 int ret = mouse_command(PSMOUSE_CMD_GETID, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400144 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500145 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400146 return;
147 }
148 regs->bh = param[0];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400149 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500150}
151
152// Initialize Mouse
153static void
154mouse_15c205(struct bregs *regs)
155{
156 if (regs->bh != 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500157 set_code_invalid(regs, RET_EINTERFACE);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500158 return;
159 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500160 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400161 SET_EBDA(ebda_seg, mouse_flag1, 0x00);
162 SET_EBDA(ebda_seg, mouse_flag2, regs->bh);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500163
164 // Reset Mouse
165 mouse_15c201(regs);
166}
167
168// Return Status
169static void
170mouse_15c20600(struct bregs *regs)
171{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400172 u8 param[3];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400173 int ret = mouse_command(PSMOUSE_CMD_GETINFO, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400174 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500175 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400176 return;
177 }
178 regs->bl = param[0];
179 regs->cl = param[1];
180 regs->dl = param[2];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400181 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500182}
183
184// Set Scaling Factor to 1:1
185static void
186mouse_15c20601(struct bregs *regs)
187{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400188 int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400189 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500190 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400191 else
192 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500193}
194
195// Set Scaling Factor to 2:1
196static void
197mouse_15c20602(struct bregs *regs)
198{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400199 int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400200 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500201 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400202 else
203 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500204}
205
206static void
207mouse_15c206XX(struct bregs *regs)
208{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500209 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500210}
211
212// Return Status & Set Scaling Factor...
213static void
214mouse_15c206(struct bregs *regs)
215{
216 switch (regs->bh) {
217 case 0x00: mouse_15c20600(regs); break;
218 case 0x01: mouse_15c20601(regs); break;
219 case 0x02: mouse_15c20602(regs); break;
220 default: mouse_15c206XX(regs); break;
221 }
222}
223
224// Set Mouse Handler Address
225static void
226mouse_15c207(struct bregs *regs)
227{
Kevin O'Connor9f985422009-09-09 11:34:39 -0400228 struct segoff_s farptr = SEGOFF(regs->es, regs->bx);
Kevin O'Connor08815372008-12-29 21:16:31 -0500229 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400230 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor9f985422009-09-09 11:34:39 -0400231 if (! farptr.segoff) {
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500232 /* remove handler */
233 if ((mouse_flags_2 & 0x80) != 0) {
234 mouse_flags_2 &= ~0x80;
Kevin O'Connor9ead6fc2011-05-07 14:19:29 -0400235 mouse_command(PSMOUSE_CMD_DISABLE, NULL);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500236 }
237 } else {
238 /* install handler */
239 mouse_flags_2 |= 0x80;
240 }
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400241 SET_EBDA(ebda_seg, mouse_flag2, mouse_flags_2);
242 SET_EBDA(ebda_seg, far_call_pointer, farptr);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400243 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500244}
245
246static void
247mouse_15c2XX(struct bregs *regs)
248{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500249 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500250}
251
252void
253handle_15c2(struct bregs *regs)
254{
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500255 //debug_stub(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500256
Kevin O'Connor57877482009-12-09 21:00:41 -0500257 if (! CONFIG_MOUSE) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500258 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500259 return;
260 }
261
262 switch (regs->al) {
263 case 0x00: mouse_15c200(regs); break;
264 case 0x01: mouse_15c201(regs); break;
265 case 0x02: mouse_15c202(regs); break;
266 case 0x03: mouse_15c203(regs); break;
267 case 0x04: mouse_15c204(regs); break;
268 case 0x05: mouse_15c205(regs); break;
269 case 0x06: mouse_15c206(regs); break;
270 case 0x07: mouse_15c207(regs); break;
271 default: mouse_15c2XX(regs); break;
272 }
273}
274
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400275static void
276invoke_mouse_handler(u16 ebda_seg)
277{
278 u16 status = GET_EBDA(ebda_seg, mouse_data[0]);
279 u16 X = GET_EBDA(ebda_seg, mouse_data[1]);
280 u16 Y = GET_EBDA(ebda_seg, mouse_data[2]);
281
282 struct segoff_s func = GET_EBDA(ebda_seg, far_call_pointer);
283 dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n"
284 , status, X, Y, func.seg, func.offset);
285
286 asm volatile(
287 "pushl %%ebp\n"
288 "sti\n"
289
290 "pushl %0\n"
291 "pushw %w1\n" // status
292 "pushw %w2\n" // X
293 "pushw %w3\n" // Y
294 "pushw $0\n" // Z
295 "lcallw *8(%%esp)\n"
296 "addl $12, %%esp\n"
297
298 "cli\n"
299 "cld\n"
300 "popl %%ebp"
301 : "+a"(func.segoff), "+c"(status), "+d"(X), "+b"(Y)
302 :
303 : "edi", "esi", "cc", "memory");
304}
305
Kevin O'Connorac7eb5e2012-05-28 14:42:16 -0400306void
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500307process_mouse(u8 data)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500308{
Kevin O'Connor57877482009-12-09 21:00:41 -0500309 if (!CONFIG_MOUSE)
310 return;
311
Kevin O'Connor08815372008-12-29 21:16:31 -0500312 u16 ebda_seg = get_ebda_seg();
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400313 u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1);
314 u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500315
Kevin O'Connor65e63422008-07-19 14:12:32 -0400316 if (! (mouse_flags_2 & 0x80))
317 // far call handler not installed
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500318 return;
319
320 u8 package_count = mouse_flags_2 & 0x07;
321 u8 index = mouse_flags_1 & 0x07;
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400322 SET_EBDA(ebda_seg, mouse_data[index], data);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500323
324 if ((index+1) < package_count) {
325 mouse_flags_1++;
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400326 SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500327 return;
328 }
329
Kevin O'Connor4bc49972012-05-13 22:58:08 -0400330 SET_EBDA(ebda_seg, mouse_flag1, 0);
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400331 stack_hop_back(ebda_seg, 0, invoke_mouse_handler);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500332}