blob: 09273b0e6220c2f09414c21a4ae5a278a2e2848a [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'Connor15c1f222008-06-12 22:59:43 -04009#include "util.h" // debug_isr
Kevin O'Connord21c0892008-11-26 17:02:43 -050010#include "pic.h" // eoi_pic2
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "bregs.h" // struct bregs
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040012#include "ps2port.h" // ps2_mouse_command
Kevin O'Connor0e885762010-05-01 22:14:40 -040013#include "usb-hid.h" // usb_mouse_command
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050014
Kevin O'Connorf54c1502008-06-14 15:56:16 -040015void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050016mouse_setup(void)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040017{
Kevin O'Connor57877482009-12-09 21:00:41 -050018 if (! CONFIG_MOUSE)
Kevin O'Connorf54c1502008-06-14 15:56:16 -040019 return;
20 dprintf(3, "init mouse\n");
21 // pointing device installed
22 SETBITS_BDA(equipment_list_flags, 0x04);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040023}
24
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040025static inline int
26mouse_command(int command, u8 *param)
27{
Kevin O'Connor0e885762010-05-01 22:14:40 -040028 if (usb_mouse_active())
29 return usb_mouse_command(command, param);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040030 return ps2_mouse_command(command, param);
31}
32
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050033#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
Kevin O'Connor3b897192008-07-20 10:08:59 -040040static int
Kevin O'Connor6704cf92010-03-13 18:51:46 -050041disable_mouse(u16 ebda_seg)
Kevin O'Connor3b897192008-07-20 10:08:59 -040042{
Kevin O'Connor6704cf92010-03-13 18:51:46 -050043 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
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040048 return mouse_command(PSMOUSE_CMD_DISABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040049}
50
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050051// Disable Mouse
52static void
53mouse_15c20000(struct bregs *regs)
54{
Kevin O'Connor6704cf92010-03-13 18:51:46 -050055 u16 ebda_seg = get_ebda_seg();
56 int ret = disable_mouse(ebda_seg);
Kevin O'Connor3b897192008-07-20 10:08:59 -040057 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050058 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040059 else
60 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050061}
62
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050063// Enable Mouse
64static void
65mouse_15c20001(struct bregs *regs)
66{
Kevin O'Connor6704cf92010-03-13 18:51:46 -050067 u16 ebda_seg = get_ebda_seg();
68 u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050069 if ((mouse_flags_2 & 0x80) == 0) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050070 set_code_invalid(regs, RET_ENOHANDLER);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050071 return;
72 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040073
Kevin O'Connor6704cf92010-03-13 18:51:46 -050074 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
Kevin O'Connordd5a8a62010-05-01 19:59:34 -040079 int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -040080 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -050081 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -040082 else
Kevin O'Connor6c781222008-03-09 12:19:23 -040083 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050084}
85
86static void
87mouse_15c200XX(struct bregs *regs)
88{
Kevin O'Connordfefeb52009-12-13 13:04:17 -050089 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050090}
91
92// Disable/Enable Mouse
93static void
94mouse_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
104static void
105mouse_15c201(struct bregs *regs)
106{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400107 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400108 int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param);
Kevin O'Connorb44a8522009-01-17 23:30:01 -0500109 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500110 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500111 return;
112 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400113 regs->bl = param[0];
114 regs->bh = param[1];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400115 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500116}
117
118// Set Sample Rate
119static void
120mouse_15c202(struct bregs *regs)
121{
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500122 static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
Kevin O'Connor3b897192008-07-20 10:08:59 -0400123 if (regs->bh >= ARRAY_SIZE(sample_rates)) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500124 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500125 return;
126 }
Kevin O'Connor15157a32008-12-13 11:10:37 -0500127 u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400128 int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400129 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500130 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400131 else
132 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500133}
134
135// Set Resolution
136static void
137mouse_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
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500144 if (regs->bh >= 4) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500145 set_code_invalid(regs, RET_EINVINPUT);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400146 return;
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500147 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400148 u8 param = regs->bh;
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400149 int ret = mouse_command(PSMOUSE_CMD_SETRES, &param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400150 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500151 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400152 else
153 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500154}
155
156// Get Device ID
157static void
158mouse_15c204(struct bregs *regs)
159{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400160 u8 param[2];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400161 int ret = mouse_command(PSMOUSE_CMD_GETID, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400162 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500163 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400164 return;
165 }
166 regs->bh = param[0];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400167 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500168}
169
170// Initialize Mouse
171static void
172mouse_15c205(struct bregs *regs)
173{
174 if (regs->bh != 3) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500175 set_code_invalid(regs, RET_EINTERFACE);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500176 return;
177 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500178 u16 ebda_seg = get_ebda_seg();
179 SET_EBDA2(ebda_seg, mouse_flag1, 0x00);
180 SET_EBDA2(ebda_seg, mouse_flag2, regs->bh);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500181
182 // Reset Mouse
183 mouse_15c201(regs);
184}
185
186// Return Status
187static void
188mouse_15c20600(struct bregs *regs)
189{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400190 u8 param[3];
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400191 int ret = mouse_command(PSMOUSE_CMD_GETINFO, param);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400192 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500193 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400194 return;
195 }
196 regs->bl = param[0];
197 regs->cl = param[1];
198 regs->dl = param[2];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400199 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500200}
201
202// Set Scaling Factor to 1:1
203static void
204mouse_15c20601(struct bregs *regs)
205{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400206 int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400207 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500208 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400209 else
210 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500211}
212
213// Set Scaling Factor to 2:1
214static void
215mouse_15c20602(struct bregs *regs)
216{
Kevin O'Connordd5a8a62010-05-01 19:59:34 -0400217 int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400218 if (ret)
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500219 set_code_invalid(regs, RET_ENEEDRESEND);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400220 else
221 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500222}
223
224static void
225mouse_15c206XX(struct bregs *regs)
226{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500227 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500228}
229
230// Return Status & Set Scaling Factor...
231static void
232mouse_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
243static void
244mouse_15c207(struct bregs *regs)
245{
Kevin O'Connor9f985422009-09-09 11:34:39 -0400246 struct segoff_s farptr = SEGOFF(regs->es, regs->bx);
Kevin O'Connor08815372008-12-29 21:16:31 -0500247 u16 ebda_seg = get_ebda_seg();
248 u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
Kevin O'Connor9f985422009-09-09 11:34:39 -0400249 if (! farptr.segoff) {
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500250 /* remove handler */
251 if ((mouse_flags_2 & 0x80) != 0) {
252 mouse_flags_2 &= ~0x80;
Kevin O'Connor6704cf92010-03-13 18:51:46 -0500253 disable_mouse(ebda_seg);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500254 }
255 } else {
256 /* install handler */
257 mouse_flags_2 |= 0x80;
258 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500259 SET_EBDA2(ebda_seg, mouse_flag2, mouse_flags_2);
260 SET_EBDA2(ebda_seg, far_call_pointer, farptr);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400261 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500262}
263
264static void
265mouse_15c2XX(struct bregs *regs)
266{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500267 set_code_unimplemented(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500268}
269
270void
271handle_15c2(struct bregs *regs)
272{
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500273 //debug_stub(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500274
Kevin O'Connor57877482009-12-09 21:00:41 -0500275 if (! CONFIG_MOUSE) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500276 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500277 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
Kevin O'Connor84a4d4b2010-02-11 22:32:12 -0500293void noinline
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500294process_mouse(u8 data)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500295{
Kevin O'Connor57877482009-12-09 21:00:41 -0500296 if (!CONFIG_MOUSE)
297 return;
298
Kevin O'Connor08815372008-12-29 21:16:31 -0500299 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);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500302
Kevin O'Connor65e63422008-07-19 14:12:32 -0400303 if (! (mouse_flags_2 & 0x80))
304 // far call handler not installed
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500305 return;
306
307 u8 package_count = mouse_flags_2 & 0x07;
308 u8 index = mouse_flags_1 & 0x07;
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500309 SET_EBDA2(ebda_seg, mouse_data[index], data);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500310
311 if ((index+1) < package_count) {
312 mouse_flags_1++;
Kevin O'Connor08815372008-12-29 21:16:31 -0500313 SET_EBDA2(ebda_seg, mouse_flag1, mouse_flags_1);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500314 return;
315 }
316
Kevin O'Connor08815372008-12-29 21:16:31 -0500317 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);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500321
Kevin O'Connor9f985422009-09-09 11:34:39 -0400322 struct segoff_s func = GET_EBDA2(ebda_seg, far_call_pointer);
Kevin O'Connor4ed378a2010-03-13 22:29:55 -0500323 dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n"
324 , status, X, Y, func.seg, func.offset);
Kevin O'Connora83ff552009-01-01 21:00:59 -0500325
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500326 asm volatile(
Kevin O'Connor4ed378a2010-03-13 22:29:55 -0500327 "pushl %%ebp\n"
Kevin O'Connor4ebc0b72009-03-01 12:31:57 -0500328 "sti\n"
329
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500330 "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"
Kevin O'Connor4ebc0b72009-03-01 12:31:57 -0500337
338 "cli\n"
Kevin O'Connor7a558e42008-03-11 20:38:33 -0400339 "cld\n"
Kevin O'Connor4ed378a2010-03-13 22:29:55 -0500340 "popl %%ebp"
341 : "+a"(func.segoff), "+c"(status), "+d"(X), "+b"(Y)
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500342 :
Kevin O'Connor4ed378a2010-03-13 22:29:55 -0500343 : "edi", "esi", "cc", "memory");
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500344}