/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: subsystems/mvdm/ntvdm/bios/bios32/moubios32.c * PURPOSE: VDM 32-bit PS/2 Mouse BIOS * PROGRAMMERS: Aleksandar Andrejevic * Hermes Belusca-Maito (hermes.belusca@sfr.fr) * * NOTE: Based from VirtualBox OSE ROM BIOS, and SeaBIOS. */ /* INCLUDES *******************************************************************/ #include "ntvdm.h" #define NDEBUG #include #include "emulator.h" #include "cpu/cpu.h" // for EMULATOR_FLAG_CF #include "moubios32.h" #include "bios32p.h" #include "io.h" #include "hardware/mouse.h" #include "hardware/ps2.h" /* PRIVATE VARIABLES **********************************************************/ #define MOUSE_IRQ_INT 0x74 static BOOLEAN MouseEnabled = FALSE; static DWORD OldIrqHandler; /* * Far pointer to a device handler. In compatible PS/2, it is stored in the EBDA. * * See Ralf Brown: http://www.ctyme.com/intr/rb-1603.htm * for more information. In particular: * when the subroutine is called, it is given 4 WORD values on the stack; * the handler should return with a FAR return without popping the stack. */ static ULONG DeviceHandler = 0; /* PRIVATE FUNCTIONS **********************************************************/ static VOID DisableMouseInt(VOID) { BYTE ControllerConfig; /* Clear the mouse queue */ while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 /* Disable mouse interrupt and events */ IOWriteB(PS2_CONTROL_PORT, 0x20); ControllerConfig = IOReadB(PS2_DATA_PORT); ControllerConfig &= ~0x02; // Turn off IRQ12 ControllerConfig |= 0x20; // Disable mouse clock line IOWriteB(PS2_CONTROL_PORT, 0x60); IOWriteB(PS2_DATA_PORT, ControllerConfig); } static VOID EnableMouseInt(VOID) { BYTE ControllerConfig; /* Clear the mouse queue */ while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 /* Enable mouse interrupt and events */ IOWriteB(PS2_CONTROL_PORT, 0x20); ControllerConfig = IOReadB(PS2_DATA_PORT); ControllerConfig |= 0x02; // Turn on IRQ12 ControllerConfig &= ~0x20; // Enable mouse clock line IOWriteB(PS2_CONTROL_PORT, 0x60); IOWriteB(PS2_DATA_PORT, ControllerConfig); } static inline VOID SendMouseCommand(UCHAR Command) { /* Clear the mouse queue */ while (PS2PortQueueRead(1)) ; // NOTE: Should be a IOReadB! But see r67231 /* Send the command */ IOWriteB(PS2_CONTROL_PORT, 0xD4); IOWriteB(PS2_DATA_PORT, Command); } static inline UCHAR ReadMouseData(VOID) { PS2PortQueueRead(1); // NOTE: Should be a IOReadB! But see r67231 return IOReadB(PS2_DATA_PORT); } static VOID BiosMouseEnable(VOID) { if (MouseEnabled) return; MouseEnabled = TRUE; /* Get the old IRQ handler */ OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT]; /* Set the IRQ handler */ //RegisterInt32(MAKELONG(FIELD_OFFSET(MOUSE_DRIVER, MouseIrqInt16Stub), MouseDataSegment), // MOUSE_IRQ_INT, DosMouseIrq, NULL); } static VOID BiosMouseDisable(VOID) { if (!MouseEnabled) return; /* Restore the old IRQ handler */ // ((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler; MouseEnabled = FALSE; } // Mouse IRQ 12 static VOID WINAPI BiosMouseIrq(LPWORD Stack) { DPRINT1("PS/2 Mouse IRQ! DeviceHandler = 0x%04X:0x%04X\n", HIWORD(DeviceHandler), LOWORD(DeviceHandler)); if (DeviceHandler != 0) { /* * Prepare the stack for the mouse device handler: * push Status, X and Y data, and a zero word. */ setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Status setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // X data (high byte = 0) setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Y data (high byte = 0) setSP(getSP() - sizeof(WORD)); *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = 0; // Zero /* Call the device handler */ RunCallback16(&BiosContext, DeviceHandler); /* Pop the stack */ setSP(getSP() + 4*sizeof(WORD)); } PicIRQComplete(LOBYTE(Stack[STACK_INT_NUM])); } VOID BiosMousePs2Interface(LPWORD Stack) { /* Disable mouse interrupt and events */ DisableMouseInt(); switch (getAL()) { /* Enable / Disable */ case 0x00: { UCHAR State = getBH(); if (State > 2) { /* Invalid function */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } if (State == 0x00) { BiosMouseDisable(); /* Disable packet reporting */ SendMouseCommand(0xF5); } else // if (State == 0x01) { /* Check for the presence of the device handler */ if (DeviceHandler == 0) { /* No device handler installed */ setAH(0x05); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } BiosMouseEnable(); /* Enable packet reporting */ SendMouseCommand(0xF4); } if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Initialize */ case 0x05: { // Fall through } /* Reset */ case 0x01: { UCHAR Answer; SendMouseCommand(0xFF); Answer = ReadMouseData(); /* A "Resend" signal (0xFE) is sent if no mouse is attached */ if (Answer == 0xFE) { /* Resend */ setAH(0x04); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } else if (Answer != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } setBL(ReadMouseData()); // Should be MOUSE_BAT_SUCCESS setBH(ReadMouseData()); // Mouse ID /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Set Sampling Rate */ case 0x02: { UCHAR SampleRate = 0; switch (getBH()) { case 0x00: SampleRate = 10; break; // 10 reports/sec case 0x01: SampleRate = 20; break; // 20 " " case 0x02: SampleRate = 40; break; // 40 " " case 0x03: SampleRate = 60; break; // 60 " " case 0x04: SampleRate = 80; break; // 80 " " case 0x05: SampleRate = 100; break; // 100 " " case 0x06: SampleRate = 200; break; // 200 " " default: SampleRate = 0; } if (SampleRate == 0) { /* Invalid input */ setAH(0x02); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } SendMouseCommand(0xF3); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } SendMouseCommand(SampleRate); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Set Resolution */ case 0x03: { UCHAR Resolution = getBH(); /* * 0: 25 dpi, 1 count per millimeter * 1: 50 dpi, 2 counts per millimeter * 2: 100 dpi, 4 counts per millimeter * 3: 200 dpi, 8 counts per millimeter */ if (Resolution > 3) { /* Invalid input */ setAH(0x02); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } SendMouseCommand(0xE8); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } SendMouseCommand(Resolution); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Get Type */ case 0x04: { SendMouseCommand(0xF2); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } setBH(ReadMouseData()); /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Extended Commands (Return Status and Set Scaling Factor) */ case 0x06: { UCHAR Command = getBH(); switch (Command) { /* Return Status */ case 0x00: { SendMouseCommand(0xE9); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } setBL(ReadMouseData()); // Status setCL(ReadMouseData()); // Resolution setDL(ReadMouseData()); // Sample rate /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Set Scaling Factor to 1:1 */ case 0x01: /* Set Scaling Factor to 2:1 */ case 0x02: { SendMouseCommand(Command == 0x01 ? 0xE6 : 0xE7); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } default: { /* Invalid function */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } } break; } /* Set Device Handler Address */ case 0x07: { /* ES:BX == 0000h:0000h removes the device handler */ DeviceHandler = MAKELONG(getBX(), getES()); /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Write to Pointer Port */ case 0x08: { SendMouseCommand(getBL()); if (ReadMouseData() != MOUSE_ACK) { /* Failure */ setAH(0x03); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; break; } /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } /* Read from Pointer Port */ case 0x09: { setBL(ReadMouseData()); setCL(ReadMouseData()); setDL(ReadMouseData()); /* Success */ setAH(0x00); Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF; break; } default: { DPRINT1("INT 15h, AH = C2h, AL = %02Xh NOT IMPLEMENTED\n", getAL()); /* Unknown function */ setAH(0x01); Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF; } } /* Reenable mouse interrupt and events */ EnableMouseInt(); } /* PUBLIC FUNCTIONS ***********************************************************/ VOID MouseBios32Post(VOID) { UCHAR Answer; /* Initialize PS/2 mouse port */ // Enable the port IOWriteB(PS2_CONTROL_PORT, 0xA8); /* Detect mouse presence by attempting a reset */ SendMouseCommand(0xFF); Answer = ReadMouseData(); /* A "Resend" signal (0xFE) is sent if no mouse is attached */ if (Answer == 0xFE) { DPRINT1("No mouse present!\n"); } else if (Answer != MOUSE_ACK) { DPRINT1("Mouse reset failure!\n"); } else { /* Mouse present, try to completely enable it */ // FIXME: The following is temporary until // this is moved into the mouse driver!! /* Enable packet reporting */ SendMouseCommand(0xF4); if (ReadMouseData() != MOUSE_ACK) { DPRINT1("Failed to enable mouse!\n"); } else { /* Enable mouse interrupt and events */ EnableMouseInt(); } } /* No mouse driver available so far */ RegisterBiosInt32(0x33, NULL); /* Set up the HW vector interrupts */ EnableHwIRQ(12, BiosMouseIrq); } BOOLEAN MouseBiosInitialize(VOID) { return TRUE; } VOID MouseBios32Cleanup(VOID) { }