/** Get mouse packet . Only care first 3 bytes @param MouseDev Pointer of PS2 Mouse Private Data Structure @retval EFI_NOT_READY Mouse Device not ready to input data packet, or some error happened during getting the packet @retval EFI_SUCCESS The data packet is gotten successfully. **/ EFI_STATUS PS2MouseGetPacket ( PS2_MOUSE_DEV *MouseDev ) { EFI_STATUS Status; BOOLEAN KeyboardEnable; UINT8 Packet[PS2_PACKET_LENGTH]; UINT8 Data; UINTN Count; UINTN State; INT16 RelativeMovementX; INT16 RelativeMovementY; BOOLEAN LButton; BOOLEAN RButton; KeyboardEnable = FALSE; Count = 1; State = PS2_READ_BYTE_ONE; // // State machine to get mouse packet // while (1) { switch (State) { case PS2_READ_BYTE_ONE: // // Read mouse first byte data, if failed, immediately return // KbcDisableAux (); Status = PS2MouseRead (&Data, &Count, State); if (EFI_ERROR (Status)) { KbcEnableAux (); return EFI_NOT_READY; } if (Count != 1) { KbcEnableAux (); return EFI_NOT_READY; } if (IS_PS2_SYNC_BYTE (Data)) { Packet[0] = Data; State = PS2_READ_DATA_BYTE; CheckKbStatus (&KeyboardEnable); KbcDisableKb (); KbcEnableAux (); } break; case PS2_READ_DATA_BYTE: Count = 2; Status = PS2MouseRead ((Packet + 1), &Count, State); if (EFI_ERROR (Status)) { if (KeyboardEnable) { KbcEnableKb (); } return EFI_NOT_READY; } if (Count != 2) { if (KeyboardEnable) { KbcEnableKb (); } return EFI_NOT_READY; } State = PS2_PROCESS_PACKET; break; case PS2_PROCESS_PACKET: if (KeyboardEnable) { KbcEnableKb (); } // // Decode the packet // RelativeMovementX = Packet[1]; RelativeMovementY = Packet[2]; // // Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 // Byte 0 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle Btn | Right Btn | Left Btn // Byte 1 | 8 bit X Movement // Byte 2 | 8 bit Y Movement // // X sign bit + 8 bit X Movement : 9-bit signed twos complement integer that presents the relative displacement of the device in the X direction since the last data transmission. // Y sign bit + 8 bit Y Movement : Same as X sign bit + 8 bit X Movement. // // // First, Clear X and Y high 8 bits // RelativeMovementX = (INT16) (RelativeMovementX & 0xFF); RelativeMovementY = (INT16) (RelativeMovementY & 0xFF); // // Second, if the 9-bit signed twos complement integer is negative, set the high 8 bit 0xff // if ((Packet[0] & 0x10) != 0) { RelativeMovementX = (INT16) (RelativeMovementX | 0xFF00); } if ((Packet[0] & 0x20) != 0) { RelativeMovementY = (INT16) (RelativeMovementY | 0xFF00); } RButton = (UINT8) (Packet[0] & 0x2); LButton = (UINT8) (Packet[0] & 0x1); // // Update mouse state // MouseDev->State.RelativeMovementX += RelativeMovementX; MouseDev->State.RelativeMovementY -= RelativeMovementY; MouseDev->State.RightButton = (UINT8) (RButton ? TRUE : FALSE); MouseDev->State.LeftButton = (UINT8) (LButton ? TRUE : FALSE); MouseDev->StateChanged = TRUE; return EFI_SUCCESS; } } }
/** Start this driver on ControllerHandle by opening a IsaIo protocol, creating PS2_MOUSE_ABSOLUTE_POINTER_DEV device and install gEfiAbsolutePointerProtocolGuid finally. @param This Protocol instance pointer. @param ControllerHandle Handle of device to bind driver to @param RemainingDevicePath Optional parameter use to pick a specific child device to start. @retval EFI_SUCCESS This driver is added to ControllerHandle @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle @retval other This driver does not support this device **/ EFI_STATUS EFIAPI PS2MouseAbsolutePointerDriverStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_STATUS EmptyStatus; EFI_ISA_IO_PROTOCOL *IsaIo; PS2_MOUSE_ABSOLUTE_POINTER_DEV *MouseAbsolutePointerDev; UINT8 Data; EFI_TPL OldTpl; EFI_STATUS_CODE_VALUE StatusCode; EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; StatusCode = 0; MouseAbsolutePointerDev = NULL; IsaIo = NULL; // // Open the device path protocol // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &ParentDevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } // // Report that the keyboard is being enabled // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_ENABLE, ParentDevicePath ); // // Get the ISA I/O Protocol on Controller's handle // Status = gBS->OpenProtocol ( Controller, &gEfiIsaIoProtocolGuid, (VOID **) &IsaIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_INVALID_PARAMETER; } // // Raise TPL to avoid keyboard operation impact // OldTpl = gBS->RaiseTPL (TPL_NOTIFY); // // Allocate private data // MouseAbsolutePointerDev = AllocateZeroPool (sizeof (PS2_MOUSE_ABSOLUTE_POINTER_DEV)); if (MouseAbsolutePointerDev == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Setup the device instance // MouseAbsolutePointerDev->Signature = PS2_MOUSE_ABSOLUTE_POINTER_DEV_SIGNATURE; MouseAbsolutePointerDev->Handle = Controller; MouseAbsolutePointerDev->SampleRate = SampleRate20; MouseAbsolutePointerDev->Resolution = MouseResolution4; MouseAbsolutePointerDev->Scaling = Scaling1; MouseAbsolutePointerDev->DataPackageSize = 3; MouseAbsolutePointerDev->IsaIo = IsaIo; MouseAbsolutePointerDev->DevicePath = ParentDevicePath; // // Resolution = 4 counts/mm // MouseAbsolutePointerDev->Mode.AbsoluteMaxX = 1024; MouseAbsolutePointerDev->Mode.AbsoluteMinX = 0; MouseAbsolutePointerDev->Mode.AbsoluteMaxY = 798; MouseAbsolutePointerDev->Mode.AbsoluteMinY = 0; MouseAbsolutePointerDev->Mode.AbsoluteMaxZ = 0; MouseAbsolutePointerDev->Mode.AbsoluteMinZ = 0; MouseAbsolutePointerDev->Mode.Attributes = 0x03; MouseAbsolutePointerDev->AbsolutePointerProtocol.Reset = MouseAbsolutePointerReset; MouseAbsolutePointerDev->AbsolutePointerProtocol.GetState = MouseAbsolutePointerGetState; MouseAbsolutePointerDev->AbsolutePointerProtocol.Mode = &(MouseAbsolutePointerDev->Mode); // // Initialize keyboard controller if necessary // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_MOUSE_PC_SELF_TEST, ParentDevicePath ); IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data); if ((Data & KBC_SYSF) != KBC_SYSF) { Status = KbcSelfTest (IsaIo); if (EFI_ERROR (Status)) { StatusCode = EFI_PERIPHERAL_MOUSE | EFI_P_EC_CONTROLLER_ERROR; goto ErrorExit; } } KbcEnableAux (IsaIo); REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_PRESENCE_DETECT, ParentDevicePath ); // // Reset the mouse // Status = MouseAbsolutePointerDev->AbsolutePointerProtocol.Reset ( &MouseAbsolutePointerDev->AbsolutePointerProtocol, FeaturePcdGet (PcdPs2MouseExtendedVerification) ); if (EFI_ERROR (Status)) { // // mouse not connected // Status = EFI_SUCCESS; StatusCode = EFI_PERIPHERAL_MOUSE | EFI_P_EC_NOT_DETECTED; goto ErrorExit; } REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_DETECTED, ParentDevicePath ); // // Setup the WaitForKey event // Status = gBS->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, MouseAbsolutePointerWaitForInput, MouseAbsolutePointerDev, &((MouseAbsolutePointerDev->AbsolutePointerProtocol).WaitForInput) ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Setup a periodic timer, used to poll mouse state // Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, PollMouseAbsolutePointer, MouseAbsolutePointerDev, &MouseAbsolutePointerDev->TimerEvent ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Start timer to poll mouse (100 samples per second) // Status = gBS->SetTimer (MouseAbsolutePointerDev->TimerEvent, TimerPeriodic, 100000); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } MouseAbsolutePointerDev->ControllerNameTable = NULL; AddUnicodeString2 ( "eng", gPs2MouseAbsolutePointerComponentName.SupportedLanguages, &MouseAbsolutePointerDev->ControllerNameTable, L"Faked PS/2 Touchpad Device", TRUE ); AddUnicodeString2 ( "en", gPs2MouseAbsolutePointerComponentName2.SupportedLanguages, &MouseAbsolutePointerDev->ControllerNameTable, L"Faked PS/2 Touchpad Device", FALSE ); // // Install protocol interfaces for the mouse device. // Status = gBS->InstallMultipleProtocolInterfaces ( &Controller, &gEfiAbsolutePointerProtocolGuid, &MouseAbsolutePointerDev->AbsolutePointerProtocol, NULL ); if (EFI_ERROR (Status)) { goto ErrorExit; } gBS->RestoreTPL (OldTpl); return Status; ErrorExit: KbcDisableAux (IsaIo); if (StatusCode != 0) { REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, StatusCode, ParentDevicePath ); } if ((MouseAbsolutePointerDev != NULL) && (MouseAbsolutePointerDev->AbsolutePointerProtocol.WaitForInput != NULL)) { gBS->CloseEvent (MouseAbsolutePointerDev->AbsolutePointerProtocol.WaitForInput); } if ((MouseAbsolutePointerDev != NULL) && (MouseAbsolutePointerDev->TimerEvent != NULL)) { gBS->CloseEvent (MouseAbsolutePointerDev->TimerEvent); } if ((MouseAbsolutePointerDev != NULL) && (MouseAbsolutePointerDev->ControllerNameTable != NULL)) { FreeUnicodeStringTable (MouseAbsolutePointerDev->ControllerNameTable); } // // Since there will be no timer handler for mouse input any more, // exhaust input data just in case there is still mouse data left // EmptyStatus = EFI_SUCCESS; while (!EFI_ERROR (EmptyStatus)) { EmptyStatus = In8042Data (IsaIo, &Data); } if (MouseAbsolutePointerDev != NULL) { FreePool (MouseAbsolutePointerDev); } gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiIsaIoProtocolGuid, This->DriverBindingHandle, Controller ); gBS->RestoreTPL (OldTpl); return Status; }
/** Start this driver on ControllerHandle by opening a Sio protocol, creating PS2_MOUSE_DEV device and install gEfiSimplePointerProtocolGuid finally. @param This Protocol instance pointer. @param ControllerHandle Handle of device to bind driver to @param RemainingDevicePath Optional parameter use to pick a specific child device to start. @retval EFI_SUCCESS This driver is added to ControllerHandle @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle @retval other This driver does not support this device **/ EFI_STATUS EFIAPI PS2MouseDriverStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_STATUS EmptyStatus; EFI_SIO_PROTOCOL *Sio; PS2_MOUSE_DEV *MouseDev; UINT8 Data; EFI_TPL OldTpl; EFI_STATUS_CODE_VALUE StatusCode; EFI_DEVICE_PATH_PROTOCOL *DevicePath; StatusCode = 0; // // Open the device path protocol // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return Status; } // // Report that the keyboard is being enabled // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_ENABLE, DevicePath ); // // Get the ISA I/O Protocol on Controller's handle // Status = gBS->OpenProtocol ( Controller, &gEfiSioProtocolGuid, (VOID **) &Sio, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } // // Raise TPL to avoid keyboard operation impact // OldTpl = gBS->RaiseTPL (TPL_NOTIFY); // // Allocate private data // MouseDev = AllocateZeroPool (sizeof (PS2_MOUSE_DEV)); if (MouseDev == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Setup the device instance // MouseDev->Signature = PS2_MOUSE_DEV_SIGNATURE; MouseDev->Handle = Controller; MouseDev->SampleRate = SampleRate20; MouseDev->Resolution = MouseResolution4; MouseDev->Scaling = Scaling1; MouseDev->DataPackageSize = 3; MouseDev->DevicePath = DevicePath; // // Resolution = 4 counts/mm // MouseDev->Mode.ResolutionX = 4; MouseDev->Mode.ResolutionY = 4; MouseDev->Mode.LeftButton = TRUE; MouseDev->Mode.RightButton = TRUE; MouseDev->SimplePointerProtocol.Reset = MouseReset; MouseDev->SimplePointerProtocol.GetState = MouseGetState; MouseDev->SimplePointerProtocol.Mode = &(MouseDev->Mode); // // Initialize keyboard controller if necessary // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_MOUSE_PC_SELF_TEST, DevicePath ); Data = IoRead8 (KBC_CMD_STS_PORT); // // Fix for random hangs in System waiting for the Key if no KBC is present in BIOS. // if ((Data & (KBC_PARE | KBC_TIM)) == (KBC_PARE | KBC_TIM)) { // // If nobody decodes KBC I/O port, it will read back as 0xFF. // Check the Time-Out and Parity bit to see if it has an active KBC in system // Status = EFI_DEVICE_ERROR; StatusCode = EFI_PERIPHERAL_MOUSE | EFI_P_EC_NOT_DETECTED; goto ErrorExit; } if ((Data & KBC_SYSF) != KBC_SYSF) { Status = KbcSelfTest (); if (EFI_ERROR (Status)) { StatusCode = EFI_PERIPHERAL_MOUSE | EFI_P_EC_CONTROLLER_ERROR; goto ErrorExit; } } KbcEnableAux (); REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_PRESENCE_DETECT, DevicePath ); // // Reset the mouse // Status = MouseDev->SimplePointerProtocol.Reset ( &MouseDev->SimplePointerProtocol, FeaturePcdGet (PcdPs2MouseExtendedVerification) ); if (EFI_ERROR (Status)) { // // mouse not connected // Status = EFI_SUCCESS; StatusCode = EFI_PERIPHERAL_MOUSE | EFI_P_EC_NOT_DETECTED; goto ErrorExit; } REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, EFI_PERIPHERAL_MOUSE | EFI_P_PC_DETECTED, DevicePath ); // // Setup the WaitForKey event // Status = gBS->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, MouseWaitForInput, MouseDev, &((MouseDev->SimplePointerProtocol).WaitForInput) ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Setup a periodic timer, used to poll mouse state // Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY, PollMouse, MouseDev, &MouseDev->TimerEvent ); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } // // Start timer to poll mouse (100 samples per second) // Status = gBS->SetTimer (MouseDev->TimerEvent, TimerPeriodic, 100000); if (EFI_ERROR (Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } MouseDev->ControllerNameTable = NULL; AddUnicodeString2 ( "eng", gPs2MouseComponentName.SupportedLanguages, &MouseDev->ControllerNameTable, L"PS/2 Mouse Device", TRUE ); AddUnicodeString2 ( "en", gPs2MouseComponentName2.SupportedLanguages, &MouseDev->ControllerNameTable, L"PS/2 Mouse Device", FALSE ); // // Install protocol interfaces for the mouse device. // Status = gBS->InstallMultipleProtocolInterfaces ( &Controller, &gEfiSimplePointerProtocolGuid, &MouseDev->SimplePointerProtocol, NULL ); if (EFI_ERROR (Status)) { goto ErrorExit; } gBS->RestoreTPL (OldTpl); return Status; ErrorExit: if (Status != EFI_DEVICE_ERROR) { KbcDisableAux (); } if (StatusCode != 0) { REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_ERROR_CODE | EFI_ERROR_MINOR, StatusCode, DevicePath ); } if ((MouseDev != NULL) && (MouseDev->SimplePointerProtocol.WaitForInput != NULL)) { gBS->CloseEvent (MouseDev->SimplePointerProtocol.WaitForInput); } if ((MouseDev != NULL) && (MouseDev->TimerEvent != NULL)) { gBS->CloseEvent (MouseDev->TimerEvent); } if ((MouseDev != NULL) && (MouseDev->ControllerNameTable != NULL)) { FreeUnicodeStringTable (MouseDev->ControllerNameTable); } if (Status != EFI_DEVICE_ERROR) { // // Since there will be no timer handler for mouse input any more, // exhaust input data just in case there is still mouse data left // EmptyStatus = EFI_SUCCESS; while (!EFI_ERROR (EmptyStatus)) { EmptyStatus = In8042Data (&Data); } } if (MouseDev != NULL) { FreePool (MouseDev); } gBS->CloseProtocol ( Controller, &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseProtocol ( Controller, &gEfiSioProtocolGuid, This->DriverBindingHandle, Controller ); gBS->RestoreTPL (OldTpl); return Status; }