1 /*
2  * COPYRIGHT:       GPL - See COPYING in the top level directory
3  * PROJECT:         ReactOS Virtual DOS Machine
4  * FILE:            subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
5  * PURPOSE:         VDM 32-bit PS/2 Mouse BIOS
6  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  *
9  * NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS.
10  */
11 
12 /* INCLUDES *******************************************************************/
13 
14 #include "ntvdm.h"
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #include "emulator.h"
20 #include "cpu/cpu.h" // for EMULATOR_FLAG_CF
21 
22 #include "moubios32.h"
23 #include "bios32p.h"
24 
25 #include "io.h"
26 #include "hardware/mouse.h"
27 #include "hardware/ps2.h"
28 
29 /* PRIVATE VARIABLES **********************************************************/
30 
31 #define MOUSE_IRQ_INT   0x74
32 
33 static BOOLEAN MouseEnabled = FALSE;
34 static DWORD OldIrqHandler;
35 
36 /*
37  * Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA.
38  *
39  * See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm
40  * for more information. In particular:
41  * when the subroutine is called, it is given 4 WORD values on the stack;
42  * the handler should return with a FAR return without popping the stack.
43  */
44 static ULONG DeviceHandler = 0;
45 
46 /* PRIVATE FUNCTIONS **********************************************************/
47 
48 static VOID DisableMouseInt(VOID)
49 {
50     BYTE ControllerConfig;
51 
52     /* Clear the mouse queue */
53     while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
54 
55     /* Disable mouse interrupt and events */
56     IOWriteB(PS2_CONTROL_PORT, 0x20);
57     ControllerConfig = IOReadB(PS2_DATA_PORT);
58     ControllerConfig &= ~0x02; // Turn off IRQ12
59     ControllerConfig |=  0x20; // Disable mouse clock line
60     IOWriteB(PS2_CONTROL_PORT, 0x60);
61     IOWriteB(PS2_DATA_PORT, ControllerConfig);
62 }
63 
64 static VOID EnableMouseInt(VOID)
65 {
66     BYTE ControllerConfig;
67 
68     /* Clear the mouse queue */
69     while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
70 
71     /* Enable mouse interrupt and events */
72     IOWriteB(PS2_CONTROL_PORT, 0x20);
73     ControllerConfig = IOReadB(PS2_DATA_PORT);
74     ControllerConfig |=  0x02; // Turn on IRQ12
75     ControllerConfig &= ~0x20; // Enable mouse clock line
76     IOWriteB(PS2_CONTROL_PORT, 0x60);
77     IOWriteB(PS2_DATA_PORT, ControllerConfig);
78 }
79 
80 static inline
81 VOID SendMouseCommand(UCHAR Command)
82 {
83     /* Clear the mouse queue */
84     while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231
85 
86     /* Send the command */
87     IOWriteB(PS2_CONTROL_PORT, 0xD4);
88     IOWriteB(PS2_DATA_PORT, Command);
89 }
90 
91 static inline
92 UCHAR ReadMouseData(VOID)
93 {
94     PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231
95     return IOReadB(PS2_DATA_PORT);
96 }
97 
98 
99 static
100 VOID BiosMouseEnable(VOID)
101 {
102     if (MouseEnabled) return;
103 
104     MouseEnabled = TRUE;
105 
106     /* Get the old IRQ handler */
107     OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT];
108 
109     /* Set the IRQ handler */
110     //RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment),
111     //              MOUSE_IRQ_INT, DosMouseIrq, NULL);
112 }
113 
114 static
115 VOID BiosMouseDisable(VOID)
116 {
117     if (!MouseEnabled) return;
118 
119     /* Restore the old IRQ handler */
120     // ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
121 
122     MouseEnabled = FALSE;
123 }
124 
125 
126 // Mouse IRQ 12
127 static VOID WINAPI BiosMouseIrq(LPWORD Stack)
128 {
129     DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n",
130             HIWORD(DeviceHandler), LOWORD(DeviceHandler));
131 
132     if (DeviceHandler != 0)
133     {
134         /*
135          * Prepare the stack for the mouse device handler:
136          * push Status, X and Y data, and a zero word.
137          */
138         setSP(getSP() - sizeof(WORD));
139         *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status
140         setSP(getSP() - sizeof(WORD));
141         *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0)
142         setSP(getSP() - sizeof(WORD));
143         *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0)
144         setSP(getSP() - sizeof(WORD));
145         *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero
146 
147         /* Call the device handler */
148         RunCallback16(&BiosContext, DeviceHandler);
149 
150         /* Pop the stack */
151         setSP(getSP() + 4*sizeof(WORD));
152     }
153 
154     PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM]));
155 }
156 
157 VOID BiosMousePs2Interface(LPWORD Stack)
158 {
159     /* Disable mouse interrupt and events */
160     DisableMouseInt();
161 
162     switch (getAL())
163     {
164         /* Enable / Disable */
165         case 0x00:
166         {
167             UCHAR State = getBH();
168 
169             if (State > 2)
170             {
171                 /* Invalid function */
172                 setAH(0x01);
173                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
174                 break;
175             }
176 
177             if (State == 0x00)
178             {
179                 BiosMouseDisable();
180 
181                 /* Disable packet reporting */
182                 SendMouseCommand(0xF5);
183             }
184             else // if (State == 0x01)
185             {
186                 /* Check for the presence of the device handler */
187                 if (DeviceHandler == 0)
188                 {
189                     /* No device handler installed */
190                     setAH(0x05);
191                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
192                     break;
193                 }
194 
195                 BiosMouseEnable();
196 
197                 /* Enable packet reporting */
198                 SendMouseCommand(0xF4);
199             }
200 
201             if (ReadMouseData() != MOUSE_ACK)
202             {
203                 /* Failure */
204                 setAH(0x03);
205                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
206                 break;
207             }
208 
209             /* Success */
210             setAH(0x00);
211             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
212             break;
213         }
214 
215         /* Initialize */
216         case 0x05:
217         {
218             // Fall through
219         }
220 
221         /* Reset */
222         case 0x01:
223         {
224             UCHAR Answer;
225 
226             SendMouseCommand(0xFF);
227             Answer = ReadMouseData();
228             /* A "Resend" signal (0xFE) is sent if no mouse is attached */
229             if (Answer == 0xFE)
230             {
231                 /* Resend */
232                 setAH(0x04);
233                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
234                 break;
235             }
236             else if (Answer != MOUSE_ACK)
237             {
238                 /* Failure */
239                 setAH(0x03);
240                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
241                 break;
242             }
243 
244             setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS
245             setBH(ReadMouseData()); // Mouse ID
246 
247             /* Success */
248             setAH(0x00);
249             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
250             break;
251         }
252 
253         /* Set Sampling Rate */
254         case 0x02:
255         {
256             UCHAR SampleRate = 0;
257 
258             switch (getBH())
259             {
260                 case 0x00: SampleRate =  10; break; //  10 reports/sec
261                 case 0x01: SampleRate =  20; break; //  20    "     "
262                 case 0x02: SampleRate =  40; break; //  40    "     "
263                 case 0x03: SampleRate =  60; break; //  60    "     "
264                 case 0x04: SampleRate =  80; break; //  80    "     "
265                 case 0x05: SampleRate = 100; break; // 100    "     "
266                 case 0x06: SampleRate = 200; break; // 200    "     "
267                 default:   SampleRate =   0;
268             }
269 
270             if (SampleRate == 0)
271             {
272                 /* Invalid input */
273                 setAH(0x02);
274                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
275                 break;
276             }
277 
278             SendMouseCommand(0xF3);
279             if (ReadMouseData() != MOUSE_ACK)
280             {
281                 /* Failure */
282                 setAH(0x03);
283                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
284                 break;
285             }
286 
287             SendMouseCommand(SampleRate);
288             if (ReadMouseData() != MOUSE_ACK)
289             {
290                 /* Failure */
291                 setAH(0x03);
292                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
293                 break;
294             }
295 
296             /* Success */
297             setAH(0x00);
298             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
299             break;
300         }
301 
302         /* Set Resolution */
303         case 0x03:
304         {
305             UCHAR Resolution = getBH();
306 
307             /*
308              * 0:  25 dpi, 1 count  per millimeter
309              * 1:  50 dpi, 2 counts per millimeter
310              * 2: 100 dpi, 4 counts per millimeter
311              * 3: 200 dpi, 8 counts per millimeter
312              */
313             if (Resolution > 3)
314             {
315                 /* Invalid input */
316                 setAH(0x02);
317                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
318                 break;
319             }
320 
321             SendMouseCommand(0xE8);
322             if (ReadMouseData() != MOUSE_ACK)
323             {
324                 /* Failure */
325                 setAH(0x03);
326                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
327                 break;
328             }
329 
330             SendMouseCommand(Resolution);
331             if (ReadMouseData() != MOUSE_ACK)
332             {
333                 /* Failure */
334                 setAH(0x03);
335                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
336                 break;
337             }
338 
339             /* Success */
340             setAH(0x00);
341             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
342             break;
343         }
344 
345         /* Get Type */
346         case 0x04:
347         {
348             SendMouseCommand(0xF2);
349             if (ReadMouseData() != MOUSE_ACK)
350             {
351                 /* Failure */
352                 setAH(0x03);
353                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
354                 break;
355             }
356 
357             setBH(ReadMouseData());
358 
359             /* Success */
360             setAH(0x00);
361             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
362             break;
363         }
364 
365         /* Extended Commands (Return Status and Set Scaling Factor) */
366         case 0x06:
367         {
368             UCHAR Command = getBH();
369 
370             switch (Command)
371             {
372                 /* Return Status */
373                 case 0x00:
374                 {
375                     SendMouseCommand(0xE9);
376                     if (ReadMouseData() != MOUSE_ACK)
377                     {
378                         /* Failure */
379                         setAH(0x03);
380                         Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
381                         break;
382                     }
383 
384                     setBL(ReadMouseData()); // Status
385                     setCL(ReadMouseData()); // Resolution
386                     setDL(ReadMouseData()); // Sample rate
387 
388                     /* Success */
389                     setAH(0x00);
390                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
391                     break;
392                 }
393 
394                 /* Set Scaling Factor to 1:1 */
395                 case 0x01:
396                 /* Set Scaling Factor to 2:1 */
397                 case 0x02:
398                 {
399                     SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7);
400                     if (ReadMouseData() != MOUSE_ACK)
401                     {
402                         /* Failure */
403                         setAH(0x03);
404                         Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
405                         break;
406                     }
407 
408                     /* Success */
409                     setAH(0x00);
410                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
411                     break;
412                 }
413 
414                 default:
415                 {
416                     /* Invalid function */
417                     setAH(0x01);
418                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
419                     break;
420                 }
421             }
422 
423             break;
424         }
425 
426         /* Set Device Handler Address */
427         case 0x07:
428         {
429             /* ES:BX == 0000h:0000h removes the device handler */
430             DeviceHandler = MAKELONG(getBX(), getES());
431 
432             /* Success */
433             setAH(0x00);
434             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
435             break;
436         }
437 
438         /* Write to Pointer Port */
439         case 0x08:
440         {
441             SendMouseCommand(getBL());
442             if (ReadMouseData() != MOUSE_ACK)
443             {
444                 /* Failure */
445                 setAH(0x03);
446                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
447                 break;
448             }
449 
450             /* Success */
451             setAH(0x00);
452             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
453             break;
454         }
455 
456         /* Read from Pointer Port */
457         case 0x09:
458         {
459             setBL(ReadMouseData());
460             setCL(ReadMouseData());
461             setDL(ReadMouseData());
462 
463             /* Success */
464             setAH(0x00);
465             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
466             break;
467         }
468 
469         default:
470         {
471             DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n",
472                     getAL());
473 
474             /* Unknown function */
475             setAH(0x01);
476             Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
477         }
478     }
479 
480     /* Reenable mouse interrupt and events */
481     EnableMouseInt();
482 }
483 
484 /* PUBLIC FUNCTIONS ***********************************************************/
485 
486 VOID MouseBios32Post(VOID)
487 {
488     UCHAR Answer;
489 
490     /* Initialize PS/2 mouse port */
491     // Enable the port
492     IOWriteB(PS2_CONTROL_PORT, 0xA8);
493 
494     /* Detect mouse presence by attempting a reset */
495     SendMouseCommand(0xFF);
496     Answer = ReadMouseData();
497     /* A "Resend" signal (0xFE) is sent if no mouse is attached */
498     if (Answer == 0xFE)
499     {
500         DPRINT1("No mouse present!\n");
501     }
502     else if (Answer != MOUSE_ACK)
503     {
504         DPRINT1("Mouse reset failure!\n");
505     }
506     else
507     {
508         /* Mouse present, try to completely enable it */
509 
510         // FIXME: The following is temporary until
511         // this is moved into the mouse driver!!
512 
513         /* Enable packet reporting */
514         SendMouseCommand(0xF4);
515         if (ReadMouseData() != MOUSE_ACK)
516         {
517             DPRINT1("Failed to enable mouse!\n");
518         }
519         else
520         {
521             /* Enable mouse interrupt and events */
522             EnableMouseInt();
523         }
524     }
525 
526     /* No mouse driver available so far */
527     RegisterBiosInt32(0x33, NULL);
528 
529     /* Set up the HW vector interrupts */
530     EnableHwIRQ(12, BiosMouseIrq);
531 }
532 
533 BOOLEAN MouseBiosInitialize(VOID)
534 {
535     return TRUE;
536 }
537 
538 VOID MouseBios32Cleanup(VOID)
539 {
540 }
541