blob: 5a1b81f941c6ac8b25b775b1b9764006ad7ccad9 [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'Connor3b897192008-07-20 10:08:59 -040012#include "ps2port.h" // aux_command
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050013
Kevin O'Connorf54c1502008-06-14 15:56:16 -040014void
15mouse_setup()
16{
17 if (! CONFIG_PS2_MOUSE)
18 return;
19 dprintf(3, "init mouse\n");
20 // pointing device installed
21 SETBITS_BDA(equipment_list_flags, 0x04);
Kevin O'Connord21c0892008-11-26 17:02:43 -050022 enable_hwirq(12, entry_74);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040023}
24
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050025#define RET_SUCCESS 0x00
26#define RET_EINVFUNCTION 0x01
27#define RET_EINVINPUT 0x02
28#define RET_EINTERFACE 0x03
29#define RET_ENEEDRESEND 0x04
30#define RET_ENOHANDLER 0x05
31
Kevin O'Connor3b897192008-07-20 10:08:59 -040032static int
Kevin O'Connor08815372008-12-29 21:16:31 -050033disable_mouse(u16 ebda_seg)
Kevin O'Connor3b897192008-07-20 10:08:59 -040034{
Kevin O'Connor08815372008-12-29 21:16:31 -050035 u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
Kevin O'Connor3b897192008-07-20 10:08:59 -040036 ps2ctr |= I8042_CTR_AUXDIS;
37 ps2ctr &= ~I8042_CTR_AUXINT;
Kevin O'Connor08815372008-12-29 21:16:31 -050038 SET_EBDA2(ebda_seg, ps2ctr, ps2ctr);
Kevin O'Connor3b897192008-07-20 10:08:59 -040039
40 return aux_command(PSMOUSE_CMD_DISABLE, NULL);
41}
42
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050043// Disable Mouse
44static void
45mouse_15c20000(struct bregs *regs)
46{
Kevin O'Connor08815372008-12-29 21:16:31 -050047 u16 ebda_seg = get_ebda_seg();
48 int ret = disable_mouse(ebda_seg);
Kevin O'Connor3b897192008-07-20 10:08:59 -040049 if (ret)
50 set_code_fail(regs, RET_ENEEDRESEND);
51 else
52 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050053}
54
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050055// Enable Mouse
56static void
57mouse_15c20001(struct bregs *regs)
58{
Kevin O'Connor08815372008-12-29 21:16:31 -050059 u16 ebda_seg = get_ebda_seg();
60 u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050061 if ((mouse_flags_2 & 0x80) == 0) {
Kevin O'Connor6c781222008-03-09 12:19:23 -040062 set_code_fail(regs, RET_ENOHANDLER);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050063 return;
64 }
Kevin O'Connor3b897192008-07-20 10:08:59 -040065
Kevin O'Connor08815372008-12-29 21:16:31 -050066 u8 ps2ctr = GET_EBDA2(ebda_seg, ps2ctr);
Kevin O'Connor3b897192008-07-20 10:08:59 -040067 ps2ctr &= ~I8042_CTR_AUXDIS;
68 ps2ctr |= I8042_CTR_AUXINT;
Kevin O'Connor08815372008-12-29 21:16:31 -050069 SET_EBDA2(ebda_seg, ps2ctr, ps2ctr);
Kevin O'Connor3b897192008-07-20 10:08:59 -040070
71 int ret = aux_command(PSMOUSE_CMD_ENABLE, NULL);
72 if (ret)
73 set_code_fail(regs, RET_ENEEDRESEND);
74 else
Kevin O'Connor6c781222008-03-09 12:19:23 -040075 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050076}
77
78static void
79mouse_15c200XX(struct bregs *regs)
80{
Kevin O'Connor6c781222008-03-09 12:19:23 -040081 set_code_fail(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -050082}
83
84// Disable/Enable Mouse
85static void
86mouse_15c200(struct bregs *regs)
87{
88 switch (regs->bh) {
89 case 0x00: mouse_15c20000(regs); break;
90 case 0x01: mouse_15c20001(regs); break;
91 default: mouse_15c200XX(regs); break;
92 }
93}
94
95// Reset Mouse
96static void
97mouse_15c201(struct bregs *regs)
98{
Kevin O'Connor3b897192008-07-20 10:08:59 -040099 u8 param[2];
100 int ret = aux_command(PSMOUSE_CMD_RESET_BAT, param);
Kevin O'Connorb44a8522009-01-17 23:30:01 -0500101 if (ret) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400102 set_code_fail(regs, RET_ENEEDRESEND);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500103 return;
104 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400105 regs->bl = param[0];
106 regs->bh = param[1];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400107 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500108}
109
110// Set Sample Rate
111static void
112mouse_15c202(struct bregs *regs)
113{
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500114 static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200};
Kevin O'Connor3b897192008-07-20 10:08:59 -0400115 if (regs->bh >= ARRAY_SIZE(sample_rates)) {
116 set_code_fail(regs, RET_EINVINPUT);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500117 return;
118 }
Kevin O'Connor15157a32008-12-13 11:10:37 -0500119 u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]);
Kevin O'Connor3b897192008-07-20 10:08:59 -0400120 int ret = aux_command(PSMOUSE_CMD_SETRATE, &mouse_data1);
121 if (ret)
122 set_code_fail(regs, RET_ENEEDRESEND);
123 else
124 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500125}
126
127// Set Resolution
128static void
129mouse_15c203(struct bregs *regs)
130{
131 // BH:
132 // 0 = 25 dpi, 1 count per millimeter
133 // 1 = 50 dpi, 2 counts per millimeter
134 // 2 = 100 dpi, 4 counts per millimeter
135 // 3 = 200 dpi, 8 counts per millimeter
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500136 if (regs->bh >= 4) {
Kevin O'Connor3b897192008-07-20 10:08:59 -0400137 set_code_fail(regs, RET_EINVINPUT);
138 return;
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500139 }
Kevin O'Connor3b897192008-07-20 10:08:59 -0400140 u8 param = regs->bh;
141 int ret = aux_command(PSMOUSE_CMD_SETRES, &param);
142 if (ret)
143 set_code_fail(regs, RET_ENEEDRESEND);
144 else
145 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500146}
147
148// Get Device ID
149static void
150mouse_15c204(struct bregs *regs)
151{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400152 u8 param[2];
153 int ret = aux_command(PSMOUSE_CMD_GETID, param);
154 if (ret) {
155 set_code_fail(regs, RET_ENEEDRESEND);
156 return;
157 }
158 regs->bh = param[0];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400159 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500160}
161
162// Initialize Mouse
163static void
164mouse_15c205(struct bregs *regs)
165{
166 if (regs->bh != 3) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400167 set_code_fail(regs, RET_EINTERFACE);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500168 return;
169 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500170 u16 ebda_seg = get_ebda_seg();
171 SET_EBDA2(ebda_seg, mouse_flag1, 0x00);
172 SET_EBDA2(ebda_seg, mouse_flag2, regs->bh);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500173
174 // Reset Mouse
175 mouse_15c201(regs);
176}
177
178// Return Status
179static void
180mouse_15c20600(struct bregs *regs)
181{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400182 u8 param[3];
183 int ret = aux_command(PSMOUSE_CMD_GETINFO, param);
184 if (ret) {
185 set_code_fail(regs, RET_ENEEDRESEND);
186 return;
187 }
188 regs->bl = param[0];
189 regs->cl = param[1];
190 regs->dl = param[2];
Kevin O'Connor6c781222008-03-09 12:19:23 -0400191 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500192}
193
194// Set Scaling Factor to 1:1
195static void
196mouse_15c20601(struct bregs *regs)
197{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400198 int ret = aux_command(PSMOUSE_CMD_SETSCALE11, NULL);
199 if (ret)
200 set_code_fail(regs, RET_ENEEDRESEND);
201 else
202 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500203}
204
205// Set Scaling Factor to 2:1
206static void
207mouse_15c20602(struct bregs *regs)
208{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400209 int ret = aux_command(PSMOUSE_CMD_SETSCALE21, NULL);
210 if (ret)
211 set_code_fail(regs, RET_ENEEDRESEND);
212 else
213 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500214}
215
216static void
217mouse_15c206XX(struct bregs *regs)
218{
Kevin O'Connor3b897192008-07-20 10:08:59 -0400219 set_code_fail(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500220}
221
222// Return Status & Set Scaling Factor...
223static void
224mouse_15c206(struct bregs *regs)
225{
226 switch (regs->bh) {
227 case 0x00: mouse_15c20600(regs); break;
228 case 0x01: mouse_15c20601(regs); break;
229 case 0x02: mouse_15c20602(regs); break;
230 default: mouse_15c206XX(regs); break;
231 }
232}
233
234// Set Mouse Handler Address
235static void
236mouse_15c207(struct bregs *regs)
237{
Kevin O'Connor9f985422009-09-09 11:34:39 -0400238 struct segoff_s farptr = SEGOFF(regs->es, regs->bx);
Kevin O'Connor08815372008-12-29 21:16:31 -0500239 u16 ebda_seg = get_ebda_seg();
240 u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
Kevin O'Connor9f985422009-09-09 11:34:39 -0400241 if (! farptr.segoff) {
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500242 /* remove handler */
243 if ((mouse_flags_2 & 0x80) != 0) {
244 mouse_flags_2 &= ~0x80;
Kevin O'Connor08815372008-12-29 21:16:31 -0500245 disable_mouse(ebda_seg);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500246 }
247 } else {
248 /* install handler */
249 mouse_flags_2 |= 0x80;
250 }
Kevin O'Connor08815372008-12-29 21:16:31 -0500251 SET_EBDA2(ebda_seg, mouse_flag2, mouse_flags_2);
252 SET_EBDA2(ebda_seg, far_call_pointer, farptr);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400253 set_code_success(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500254}
255
256static void
257mouse_15c2XX(struct bregs *regs)
258{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400259 set_code_fail(regs, RET_EINVFUNCTION);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500260}
261
262void
263handle_15c2(struct bregs *regs)
264{
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500265 //debug_stub(regs);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500266
267 if (! CONFIG_PS2_MOUSE) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400268 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500269 return;
270 }
271
272 switch (regs->al) {
273 case 0x00: mouse_15c200(regs); break;
274 case 0x01: mouse_15c201(regs); break;
275 case 0x02: mouse_15c202(regs); break;
276 case 0x03: mouse_15c203(regs); break;
277 case 0x04: mouse_15c204(regs); break;
278 case 0x05: mouse_15c205(regs); break;
279 case 0x06: mouse_15c206(regs); break;
280 case 0x07: mouse_15c207(regs); break;
281 default: mouse_15c2XX(regs); break;
282 }
283}
284
285static void
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500286process_mouse(u8 data)
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500287{
Kevin O'Connor08815372008-12-29 21:16:31 -0500288 u16 ebda_seg = get_ebda_seg();
289 u8 mouse_flags_1 = GET_EBDA2(ebda_seg, mouse_flag1);
290 u8 mouse_flags_2 = GET_EBDA2(ebda_seg, mouse_flag2);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500291
Kevin O'Connor65e63422008-07-19 14:12:32 -0400292 if (! (mouse_flags_2 & 0x80))
293 // far call handler not installed
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500294 return;
295
296 u8 package_count = mouse_flags_2 & 0x07;
297 u8 index = mouse_flags_1 & 0x07;
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500298 SET_EBDA2(ebda_seg, mouse_data[index], data);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500299
300 if ((index+1) < package_count) {
301 mouse_flags_1++;
Kevin O'Connor08815372008-12-29 21:16:31 -0500302 SET_EBDA2(ebda_seg, mouse_flag1, mouse_flags_1);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500303 return;
304 }
305
306 //BX_DEBUG_INT74("int74_function: make_farcall=1\n");
Kevin O'Connor08815372008-12-29 21:16:31 -0500307 u16 status = GET_EBDA2(ebda_seg, mouse_data[0]);
308 u16 X = GET_EBDA2(ebda_seg, mouse_data[1]);
309 u16 Y = GET_EBDA2(ebda_seg, mouse_data[2]);
310 SET_EBDA2(ebda_seg, mouse_flag1, 0);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500311
Kevin O'Connor9f985422009-09-09 11:34:39 -0400312 struct segoff_s func = GET_EBDA2(ebda_seg, far_call_pointer);
Kevin O'Connora83ff552009-01-01 21:00:59 -0500313
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500314 asm volatile(
Kevin O'Connor4ebc0b72009-03-01 12:31:57 -0500315 "sti\n"
316
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500317 "pushl %0\n"
318 "pushw %w1\n" // status
319 "pushw %w2\n" // X
320 "pushw %w3\n" // Y
321 "pushw $0\n" // Z
322 "lcallw *8(%%esp)\n"
323 "addl $12, %%esp\n"
Kevin O'Connor4ebc0b72009-03-01 12:31:57 -0500324
325 "cli\n"
Kevin O'Connor7a558e42008-03-11 20:38:33 -0400326 "cld\n"
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500327 :
Kevin O'Connor9f985422009-09-09 11:34:39 -0400328 : "r"(func.segoff), "r"(status), "r"(X), "r"(Y)
Kevin O'Connora83ff552009-01-01 21:00:59 -0500329 : "cc"
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500330 );
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500331}
332
333// INT74h : PS/2 mouse hardware interrupt
Kevin O'Connor19786762008-03-05 21:09:59 -0500334void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400335handle_74()
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500336{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400337 debug_isr(DEBUG_ISR_74);
Kevin O'Connor40967022008-07-21 22:23:05 -0400338 if (! CONFIG_PS2_MOUSE)
339 goto done;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500340
Kevin O'Connor0234cd92009-01-04 12:20:02 -0500341 u8 v = inb(PORT_PS2_STATUS);
342 if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
343 != (I8042_STR_OBF|I8042_STR_AUXDATA)) {
344 dprintf(1, "mouse irq but no mouse data.\n");
345 goto done;
346 }
347 v = inb(PORT_PS2_DATA);
348
349 process_mouse(v);
Kevin O'Connor4d6dbc62008-03-02 08:43:44 -0500350
Kevin O'Connor40967022008-07-21 22:23:05 -0400351done:
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400352 eoi_pic2();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500353}