VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/ps2mouse.c

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: ps2mouse.c 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * PC BIOS - ???
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is based on:
29 *
30 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
31 *
32 * Copyright (C) 2002 MandrakeSoft S.A.
33 *
34 * MandrakeSoft S.A.
35 * 43, rue d'Aboukir
36 * 75002 Paris - France
37 * http://www.linux-mandrake.com/
38 * http://www.mandrakesoft.com/
39 *
40 * This library is free software; you can redistribute it and/or
41 * modify it under the terms of the GNU Lesser General Public
42 * License as published by the Free Software Foundation; either
43 * version 2 of the License, or (at your option) any later version.
44 *
45 * This library is distributed in the hope that it will be useful,
46 * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
48 * Lesser General Public License for more details.
49 *
50 * You should have received a copy of the GNU Lesser General Public
51 * License along with this library; if not, write to the Free Software
52 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
53 *
54 */
55
56/*
57 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
58 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
59 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
60 * a choice of LGPL license versions is made available with the language indicating
61 * that LGPLv2 or any later version may be used, or where a choice of which version
62 * of the LGPL is applied is otherwise unspecified.
63 */
64
65
66#include <stdint.h>
67#include "biosint.h"
68#include "inlines.h"
69
70
71#if DEBUG_INT15_MS
72# define BX_DEBUG_INT15_MS(...) BX_DEBUG(__VA_ARGS__)
73#else
74# define BX_DEBUG_INT15_MS(...)
75#endif
76
77#if DEBUG_INT74
78# define BX_DEBUG_INT74(...) BX_DEBUG(__VA_ARGS__)
79#else
80# define BX_DEBUG_INT74(...)
81#endif
82
83#if BX_USE_PS2_MOUSE
84
85static const char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
86
87uint8_t send_to_mouse_ctrl(uint8_t sendbyte)
88{
89 BX_DEBUG_INT15_MS("send %02x to mouse:\n", sendbyte);
90 // wait for chance to write to ctrl
91 if (inb(0x64) & 0x02)
92 BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
93 outb(0x64, 0xD4);
94 outb(0x60, sendbyte);
95 return(0);
96}
97
98
99uint8_t get_mouse_data(uint8_t __far *data)
100{
101 int retries = 10000; /* ~150ms timeout */
102 uint8_t response;
103
104 while ((inb(0x64) & 0x21) != 0x21 && retries)
105 {
106 /* Wait until the 15us refresh counter toggles. */
107 response = inb(0x61) & 0x10;
108 while((inb(0x61) & 0x10) == response)
109 ;
110 --retries;
111 }
112
113 if (!retries)
114 return(1);
115
116 response = inb(0x60);
117 *data = response;
118 return(0);
119}
120
121void set_kbd_command_byte(uint8_t command_byte)
122{
123 if (inb(0x64) & 0x02)
124 BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
125
126 outb(0x64, 0x60); // write command byte
127 outb(0x60, command_byte);
128}
129
130
131void BIOSCALL int74_function(volatile uint16_t make_farcall, volatile uint16_t Z,
132 volatile uint16_t Y, volatile uint16_t X, volatile uint16_t status)
133{
134 uint16_t ebda_seg=read_word(0x0040,0x000E);
135 uint8_t in_byte, index, package_count;
136 uint8_t mouse_flags_1, mouse_flags_2;
137
138 BX_DEBUG_INT74("entering int74_function\n");
139 make_farcall = 0;
140
141 in_byte = inb(0x64);
142 if ( (in_byte & 0x21) != 0x21 ) {
143 return;
144 }
145 in_byte = inb(0x60);
146 BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
147
148 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
149 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
150
151 if ( (mouse_flags_2 & 0x80) != 0x80 ) {
152 return;
153 }
154
155 package_count = mouse_flags_2 & 0x07;
156 index = mouse_flags_1 & 0x07;
157 write_byte(ebda_seg, 0x28 + index, in_byte);
158
159 if ( index >= package_count ) {
160 BX_DEBUG_INT74("int74_function: make_farcall=1\n");
161 status = read_byte(ebda_seg, 0x0028 + 0);
162 X = read_byte(ebda_seg, 0x0028 + 1);
163 Y = read_byte(ebda_seg, 0x0028 + 2);
164 Z = 0;
165 mouse_flags_1 = 0;
166 // check if far call handler installed
167 if (mouse_flags_2 & 0x80)
168 make_farcall = 1;
169 }
170 else {
171 mouse_flags_1++;
172 }
173 write_byte(ebda_seg, 0x0026, mouse_flags_1);
174}
175
176void BIOSCALL int15_function_mouse(pusha_regs_t regs, uint16_t ES, uint16_t DS, volatile uint16_t FLAGS)
177{
178 uint16_t ebda_seg=read_word(0x0040,0x000E);
179 uint8_t mouse_flags_1, mouse_flags_2;
180 uint16_t mouse_driver_seg;
181 uint16_t mouse_driver_offset;
182 uint8_t mouse_cmd;
183 uint8_t ret, mouse_data1, mouse_data2, mouse_data3;
184
185 BX_DEBUG_INT15_MS("int15 AX=%04x\n",regs.u.r16.ax);
186
187 // Return Codes status in AH
188 // =========================
189 // 00: success
190 // 01: invalid subfunction (AL > 7)
191 // 02: invalid input value (out of allowable range)
192 // 03: interface error
193 // 04: resend command received from mouse controller,
194 // device driver should attempt command again
195 // 05: cannot enable mouse, since no far call has been installed
196 // 80/86: mouse service not implemented
197
198 if (regs.u.r8.al > 7) {
199 BX_DEBUG_INT15_MS("unsupported subfn\n");
200 // invalid function
201 SET_CF();
202 regs.u.r8.ah = 1;
203 return;
204 }
205
206 // Valid subfn; disable AUX input and IRQ12, assume no error
207 set_kbd_command_byte(0x65);
208 CLEAR_CF();
209 regs.u.r8.ah = 0;
210
211 switch (regs.u.r8.al) {
212 case 0: // Disable/Enable Mouse
213 BX_DEBUG_INT15_MS("case 0: ");
214 if (regs.u.r8.bh > 1) {
215 BX_DEBUG_INT15_MS("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
216 // invalid subfunction
217 SET_CF();
218 regs.u.r8.ah = 1;
219 break;
220 }
221 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
222 if ( (mouse_flags_2 & 0x80) == 0 ) {
223 BX_DEBUG_INT15_MS("INT 15h C2 Enable/Disable Mouse, no far call handler\n");
224 SET_CF();
225 regs.u.r8.ah = 5; // no far call installed
226 break;
227 }
228 if (regs.u.r8.bh == 0) {
229 BX_DEBUG_INT15_MS("Disable Mouse\n");
230 mouse_cmd = 0xF5; // disable mouse command
231 } else {
232 BX_DEBUG_INT15_MS("Enable Mouse\n");
233 mouse_cmd = 0xF4; // enable mouse command
234 }
235
236 ret = send_to_mouse_ctrl(mouse_cmd); // disable mouse command
237 if (ret == 0) {
238 ret = get_mouse_data(&mouse_data1);
239 if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
240 // success
241 break;
242 }
243 }
244
245 // interface error
246 SET_CF();
247 regs.u.r8.ah = 3;
248 break;
249
250 case 5: // Initialize Mouse
251 // Valid package sizes are 1 to 8
252 if ( (regs.u.r8.bh < 1) || (regs.u.r8.bh > 8) ) {
253 SET_CF();
254 regs.u.r8.ah = 2; // invalid input
255 break;
256 }
257 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
258 mouse_flags_2 = (mouse_flags_2 & 0xf8) | (regs.u.r8.bh - 1);
259 write_byte(ebda_seg, 0x0027, mouse_flags_2);
260 // fall through!
261
262 case 1: // Reset Mouse
263 BX_DEBUG_INT15_MS("case 1 or 5:\n");
264 // clear current package byte index
265 mouse_flags_1 = read_byte(ebda_seg, 0x0026);
266 mouse_flags_1 = mouse_flags_1 & 0xf8;
267 write_byte(ebda_seg, 0x0026, mouse_flags_1);
268 ret = send_to_mouse_ctrl(0xFF); // reset mouse command
269 if (ret == 0) {
270 ret = get_mouse_data(&mouse_data3);
271 // if no mouse attached, it will return RESEND
272 if (mouse_data3 == 0xfe) {
273 SET_CF();
274 regs.u.r8.ah = 4; // resend
275 break;
276 }
277 if (mouse_data3 != 0xfa)
278 BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
279 if ( ret == 0 ) {
280 ret = get_mouse_data(&mouse_data1);
281 if ( ret == 0 ) {
282 ret = get_mouse_data(&mouse_data2);
283 if ( ret == 0 ) {
284 // success
285 regs.u.r8.bl = mouse_data1;
286 regs.u.r8.bh = mouse_data2;
287 break;
288 }
289 }
290 }
291 }
292
293 // interface error
294 SET_CF();
295 regs.u.r8.ah = 3;
296 break;
297
298 case 2: // Set Sample Rate
299 BX_DEBUG_INT15_MS("case 2:\n");
300 switch (regs.u.r8.bh) {
301 case 0: mouse_data1 = 10; break; // 10 reports/sec
302 case 1: mouse_data1 = 20; break; // 20 reports/sec
303 case 2: mouse_data1 = 40; break; // 40 reports/sec
304 case 3: mouse_data1 = 60; break; // 60 reports/sec
305 case 4: mouse_data1 = 80; break; // 80 reports/sec
306 case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
307 case 6: mouse_data1 = 200; break; // 200 reports/sec
308 default: mouse_data1 = 0;
309 }
310 if (mouse_data1 > 0) {
311 ret = send_to_mouse_ctrl(0xF3); // set sample rate command
312 if (ret == 0) {
313 ret = get_mouse_data(&mouse_data2);
314 ret = send_to_mouse_ctrl(mouse_data1);
315 ret = get_mouse_data(&mouse_data2);
316 // success
317 } else {
318 // interface error
319 SET_CF();
320 regs.u.r8.ah = 3;
321 }
322 } else {
323 // invalid input
324 SET_CF();
325 regs.u.r8.ah = 2;
326 }
327 break;
328
329 case 3: // Set Resolution
330 BX_DEBUG_INT15_MS("case 3:\n");
331 // BX:
332 // 0 = 25 dpi, 1 count per millimeter
333 // 1 = 50 dpi, 2 counts per millimeter
334 // 2 = 100 dpi, 4 counts per millimeter
335 // 3 = 200 dpi, 8 counts per millimeter
336 if (regs.u.r8.bh < 4) {
337 ret = send_to_mouse_ctrl(0xE8); // set resolution command
338 if (ret == 0) {
339 ret = get_mouse_data(&mouse_data1);
340 if (mouse_data1 != 0xfa)
341 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
342 ret = send_to_mouse_ctrl(regs.u.r8.bh);
343 ret = get_mouse_data(&mouse_data1);
344 if (mouse_data1 != 0xfa)
345 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
346 // success
347 } else {
348 // interface error
349 SET_CF();
350 regs.u.r8.ah = 3;
351 }
352 } else {
353 // invalid input
354 SET_CF();
355 regs.u.r8.ah = 2;
356 }
357 break;
358
359 case 4: // Get Device ID
360 BX_DEBUG_INT15_MS("case 4:\n");
361 ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
362 if (ret == 0) {
363 ret = get_mouse_data(&mouse_data1);
364 ret = get_mouse_data(&mouse_data2);
365 regs.u.r8.bh = mouse_data2;
366 // success
367 } else {
368 // interface error
369 SET_CF();
370 regs.u.r8.ah = 3;
371 }
372 break;
373
374 case 6: // Return Status & Set Scaling Factor...
375 BX_DEBUG_INT15_MS("case 6:\n");
376 switch (regs.u.r8.bh) {
377 case 0: // Return Status
378 ret = send_to_mouse_ctrl(0xE9); // get mouse info command
379 if (ret == 0) {
380 ret = get_mouse_data(&mouse_data1);
381 if (mouse_data1 != 0xfa)
382 BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
383 if (ret == 0) {
384 ret = get_mouse_data(&mouse_data1);
385 if ( ret == 0 ) {
386 ret = get_mouse_data(&mouse_data2);
387 if ( ret == 0 ) {
388 ret = get_mouse_data(&mouse_data3);
389 if ( ret == 0 ) {
390 regs.u.r8.bl = mouse_data1;
391 regs.u.r8.cl = mouse_data2;
392 regs.u.r8.dl = mouse_data3;
393 // success
394 break;
395 }
396 }
397 }
398 }
399 }
400
401 // interface error
402 SET_CF();
403 regs.u.r8.ah = 3;
404 break;
405
406 case 1: // Set Scaling Factor to 1:1
407 case 2: // Set Scaling Factor to 2:1
408 if (regs.u.r8.bh == 1) {
409 ret = send_to_mouse_ctrl(0xE6);
410 } else {
411 ret = send_to_mouse_ctrl(0xE7);
412 }
413 if (ret == 0) {
414 get_mouse_data(&mouse_data1);
415 ret = (mouse_data1 != 0xFA);
416 }
417 if (ret != 0) {
418 // interface error
419 SET_CF();
420 regs.u.r8.ah = 3;
421 }
422 break;
423
424 default:
425 BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
426 // invalid subfunction
427 SET_CF();
428 regs.u.r8.ah = 1;
429 }
430 break;
431
432 case 7: // Set Mouse Handler Address
433 BX_DEBUG_INT15_MS("case 7:\n");
434 mouse_driver_seg = ES;
435 mouse_driver_offset = regs.u.r16.bx;
436 write_word(ebda_seg, 0x0022, mouse_driver_offset);
437 write_word(ebda_seg, 0x0024, mouse_driver_seg);
438 mouse_flags_2 = read_byte(ebda_seg, 0x0027);
439 if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
440 /* remove handler */
441 if ( (mouse_flags_2 & 0x80) != 0 ) {
442 mouse_flags_2 &= ~0x80;
443 }
444 }
445 else {
446 /* install handler */
447 mouse_flags_2 |= 0x80;
448 }
449 write_byte(ebda_seg, 0x0027, mouse_flags_2);
450 break;
451
452 default:
453 BX_PANIC("INT 15h C2 default case entered\n");
454 // invalid subfunction
455 SET_CF();
456 regs.u.r8.ah = 1;
457 }
458 BX_DEBUG_INT15_MS("returning cf = %u, ah = %02x\n", (unsigned)GET_CF(), (unsigned)regs.u.r8.ah);
459 // Re-enable AUX input and IRQ12
460 set_kbd_command_byte(0x47);
461}
462#endif // BX_USE_PS2_MOUSE
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette