2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: services/dd/keyboard/keyboard.c
5 * PURPOSE: Keyboard driver
6 * PROGRAMMER: Victor Kirhenshtein (sauros@iname.com)
7 * Jason Filby (jasonfilby@yahoo.com)
10 /* INCLUDES ****************************************************************/
12 #include <ddk/ntddk.h>
14 #include <ntos/keyboard.h>
15 #include <ntos/minmax.h>
17 #include <ddk/ntddkbd.h>
18 #include <ddk/ntdd8042.h>
25 /* GLOBALS *******************************************************************/
31 static KEY_EVENT_RECORD kbdBuffer[KBD_BUFFER_SIZE];
32 static int bufHead,bufTail;
33 static int keysInBuffer;
35 static BYTE ledStatus;
36 static BYTE capsDown,numDown,scrollDown;
37 static DWORD ctrlKeyState;
38 static PKINTERRUPT KbdInterrupt;
40 static BOOLEAN AlreadyOpened = FALSE;
43 * PURPOSE: Current irp being processed
45 static PIRP CurrentIrp;
48 * PURPOSE: Number of keys that have been read into the current irp's buffer
50 static ULONG KeysRead;
51 static ULONG KeysRequired;
54 * Virtual key codes table
58 * * Alt+PrtSc (SysRq) = VK_EXECUTE
62 static const WORD vkTable[128]=
64 /* 00 - 07 */ 0, VK_ESCAPE, VK_1, VK_2, VK_3, VK_4, VK_5, VK_6,
65 /* 08 - 0F */ VK_7, VK_8, VK_9, VK_0, 189, 187, VK_BACK, VK_TAB,
66 /* 10 - 17 */ VK_Q, VK_W, VK_E, VK_R, VK_T, VK_Y, VK_U, VK_I,
67 /* 18 - 1F */ VK_O, VK_P, 219, 221, VK_RETURN, VK_CONTROL, VK_A, VK_S,
68 /* 20 - 27 */ VK_D, VK_F, VK_G, VK_H, VK_J, VK_K, VK_L, 186,
69 /* 28 - 2F */ 222, 192, VK_SHIFT, 220, VK_Z, VK_X, VK_C, VK_V,
70 /* 30 - 37 */ VK_B, VK_N, VK_M, 188, 190, 191, VK_SHIFT, VK_MULTIPLY,
71 /* 38 - 3F */ VK_MENU, VK_SPACE, VK_CAPITAL, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5,
72 /* 40 - 47 */ VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_NUMLOCK, VK_SCROLL, VK_HOME,
73 /* 48 - 4F */ VK_UP, VK_PRIOR, VK_SUBTRACT, VK_LEFT, VK_CLEAR, VK_RIGHT, VK_ADD, VK_END,
74 /* 50 - 57 */ VK_DOWN, VK_NEXT, VK_INSERT, VK_DELETE, VK_EXECUTE, 0, 0, VK_F11,
75 /* 58 - 5F */ VK_F12, 0, 0, 91, 92, 93, 0, 0,
76 /* 60 - 67 */ 0, 0, 0, 0, 0, 0, 0, 0,
77 /* 68 - 6F */ 0, 0, 0, 0, 0, 0, 0, 0,
78 /* 70 - 77 */ 0, 0, 0, 0, 0, 0, 0, 0,
79 /* 78 - 7F */ 0, 0, 0, 0, 0, 0, 0, VK_PAUSE
81 static const WORD vkKeypadTable[13]= /* 47 - 53 */
83 VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_SUBTRACT,
84 VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_ADD,
85 VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD0, VK_DECIMAL
90 * ASCII translation tables
93 static const BYTE asciiTable1[10]=
95 ')','!','@','#','$','%','^','&','*','('
97 static const BYTE asciiTable2[16]=
99 '0','1','2','3','4','5','6','7','8','9','*','+',0,'-','.','/'
101 static const BYTE asciiTable3[37]=
103 ';','=',',','-','.','/','`', 0, 0, 0, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107 static const BYTE asciiTable4[37]=
109 ':','+','<','_','>','?','~', 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 KdSystemDebugControl(ULONG Code);
117 static LONG DoSystemDebug = -1;
118 static BOOLEAN InSysRq = FALSE;
120 /* FUNCTIONS *****************************************************************/
122 static void KbdWrite(int addr,BYTE data)
124 * FUNCTION: Write data to keyboard
131 status=READ_PORT_UCHAR((PUCHAR)KBD_CTRL_PORT); // Wait until input buffer empty
132 } while(status & KBD_IBF);
133 WRITE_PORT_UCHAR((PUCHAR)addr,data);
136 static int KbdReadData(void)
138 * FUNCTION: Read data from port 0x60
147 status=READ_PORT_UCHAR((PUCHAR)KBD_CTRL_PORT);
148 if (!(status & KBD_OBF)) // Check if data available
150 data=READ_PORT_UCHAR((PUCHAR)KBD_DATA_PORT);
151 if (status & (KBD_GTO | KBD_PERR)) // Check for timeout error
155 return -1; // Timed out
163 static void SetKeyboardLEDs(BYTE status)
165 KbdWrite(KBD_DATA_PORT,0xED);
166 if (KbdReadData()!=KBD_ACK) // Error
168 KbdWrite(KBD_DATA_PORT,status);
177 static void ProcessScanCode(BYTE scanCode,BOOL isDown)
185 ctrlKeyState|=RIGHT_CTRL_PRESSED;
187 ctrlKeyState&=~RIGHT_CTRL_PRESSED;
192 ctrlKeyState|=LEFT_CTRL_PRESSED;
194 ctrlKeyState&=~LEFT_CTRL_PRESSED;
197 case 0x2A: // Left shift
198 case 0x36: // Right shift
200 ctrlKeyState|=SHIFT_PRESSED;
202 ctrlKeyState&=~SHIFT_PRESSED;
208 ctrlKeyState|=RIGHT_ALT_PRESSED;
210 ctrlKeyState&=~RIGHT_ALT_PRESSED;
215 ctrlKeyState|=LEFT_ALT_PRESSED;
217 ctrlKeyState&=~LEFT_ALT_PRESSED;
220 case 0x3A: // CapsLock
221 if (ctrlKeyState & CTRL_PRESSED)
228 if (ctrlKeyState & CAPSLOCK_ON)
230 ledStatus&=~KBD_LED_CAPS;
231 ctrlKeyState&=~CAPSLOCK_ON;
235 ledStatus|=KBD_LED_CAPS;
236 ctrlKeyState|=CAPSLOCK_ON;
238 SetKeyboardLEDs(ledStatus);
246 case 0x45: // NumLock
247 if (ctrlKeyState & CTRL_PRESSED)
254 if (ctrlKeyState & NUMLOCK_ON)
256 ledStatus&=~KBD_LED_NUM;
257 ctrlKeyState&=~NUMLOCK_ON;
261 ledStatus|=KBD_LED_NUM;
262 ctrlKeyState|=NUMLOCK_ON;
264 SetKeyboardLEDs(ledStatus);
272 case 0x46: // ScrollLock
273 if (ctrlKeyState & CTRL_PRESSED)
280 if (ctrlKeyState & SCROLLLOCK_ON)
282 ledStatus&=~KBD_LED_SCROLL;
283 ctrlKeyState&=~SCROLLLOCK_ON;
287 ledStatus|=KBD_LED_SCROLL;
288 ctrlKeyState|=SCROLLLOCK_ON;
290 SetKeyboardLEDs(ledStatus);
305 * Translate virtual key code to ASCII
308 static BYTE VirtualToAscii(WORD keyCode,BOOL isDown)
310 if ((ctrlKeyState & ALT_PRESSED)&&(ctrlKeyState & CTRL_PRESSED))
311 return 0; // Ctrl+Alt+char always 0
312 if ((!isDown)&&(ctrlKeyState & ALT_PRESSED))
313 return 0; // Alt+char is 0 when key is released
315 if (ctrlKeyState & CTRL_PRESSED)
317 if ((keyCode>=VK_A)&&(keyCode<=VK_Z))
318 return keyCode-VK_A+1;
328 if (ctrlKeyState & SHIFT_PRESSED)
332 if (ctrlKeyState & SHIFT_PRESSED)
336 if (ctrlKeyState & SHIFT_PRESSED)
344 if ((keyCode>=VK_A)&&(keyCode<=VK_Z))
346 if (ctrlKeyState & CAPSLOCK_ON)
347 if (ctrlKeyState & SHIFT_PRESSED)
348 return keyCode-VK_A+'a';
350 return keyCode-VK_A+'A';
352 if (ctrlKeyState & SHIFT_PRESSED)
353 return keyCode-VK_A+'A';
355 return keyCode-VK_A+'a';
358 if ((keyCode>=VK_0)&&(keyCode<=VK_9))
360 if (ctrlKeyState & SHIFT_PRESSED)
361 return asciiTable1[keyCode-VK_0];
363 return keyCode-VK_0+'0';
366 if ((keyCode>=VK_NUMPAD0)&&(keyCode<=VK_DIVIDE))
367 return asciiTable2[keyCode-VK_NUMPAD0];
369 if ((keyCode>=186)&&(keyCode<=222))
371 if (ctrlKeyState & SHIFT_PRESSED)
372 return asciiTable4[keyCode-186];
374 return asciiTable3[keyCode-186];
393 * Translate scan code to virtual key code
396 static WORD ScanToVirtual(BYTE scanCode)
398 if ((scanCode>=0x47)&&(scanCode<=0x53)&&(ctrlKeyState & NUMLOCK_ON)&&
399 (!extKey)&&(!(ctrlKeyState & SHIFT_PRESSED)))
400 return vkKeypadTable[scanCode-0x47];
401 if ((scanCode==0x35)&&(extKey)) // Gray divide
403 if ((scanCode==0x37)&&(extKey)) // Print screen
405 return vkTable[scanCode];
410 * Keyboard IRQ handler
414 KbdDpcRoutine(PKDPC Dpc,
415 PVOID DeferredContext,
416 PVOID SystemArgument1,
417 PVOID SystemArgument2)
419 PIRP Irp = (PIRP)SystemArgument2;
420 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument1;
422 if (SystemArgument1 == NULL && DoSystemDebug != -1)
424 KdSystemDebugControl(DoSystemDebug);
430 DPRINT("KbdDpcRoutine(DeviceObject %x, Irp %x)\n",
432 Irp->IoStatus.Status = STATUS_SUCCESS;
433 Irp->IoStatus.Information = 0;
434 IoCompleteRequest(Irp,IO_NO_INCREMENT);
435 IoStartNextPacket(DeviceObject,FALSE);
438 static BOOLEAN STDCALL
439 KeyboardHandler(PKINTERRUPT Interrupt,
446 PDEVICE_OBJECT deviceObject = (PDEVICE_OBJECT) Context;
447 PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
454 Status = READ_PORT_UCHAR((PUCHAR)KBD_CTRL_PORT);
455 if (!(Status & KBD_OBF))
461 thisKey=READ_PORT_UCHAR((PUCHAR)KBD_DATA_PORT);
463 // Call hook routine. May change scancode value.
464 if (deviceExtension->IsrHookCallback) {
465 BOOLEAN cont = FALSE, ret;
466 //BUG BUG: rewrite to have valid CurrentScanState!!!
467 ret = (*deviceExtension->IsrHookCallback)(
469 NULL,//&deviceExtension->CurrentInput,
470 NULL,//&deviceExtension->CurrentOutput,
472 &thisKey, //&scanCode,
474 NULL //&deviceExtension->CurrentScanState
483 if ((thisKey==0xE0)||(thisKey==0xE1)) // Extended key
485 extKey=1; // Wait for next byte
490 isDown=!(thisKey & 0x80);
493 // The keyboard maintains its own internal caps lock and num lock
494 // statuses. In caps lock mode E0 AA precedes make code and
495 // E0 2A follow break code. In num lock mode, E0 2A precedes
496 // make code and E0 AA follow break code. We maintain our own caps lock
497 // and num lock statuses, so we will just ignore these.
498 // Some keyboards have L-Shift/R-Shift modes instead of caps lock
499 // mode. If right shift pressed, E0 B6 / E0 36 pairs generated.
500 if (extKey & ((thisKey==0x2A)||(thisKey==0x36)))
506 // Check for PAUSE sequence
507 if (extKey && (lastKey==0xE1))
510 lastKey=0xFF; // Sequence is OK
515 if (extKey && (lastKey==0xFF))
519 extKey=0; // Bad sequence
522 thisKey=0x7F; // Pseudo-code for PAUSE
525 ProcessScanCode(thisKey,isDown);
527 // DbgPrint("Key: %c\n",VirtualToAscii(ScanToVirtual(thisKey),isDown));
528 // DbgPrint("Key: %x\n",ScanToVirtual(thisKey));
529 if (ScanToVirtual(thisKey) == VK_TAB && isDown)
533 else if (ScanToVirtual(thisKey) == VK_TAB && !isDown)
537 else if (InSysRq == TRUE && ScanToVirtual(thisKey) >= VK_A &&
538 ScanToVirtual(thisKey) <= VK_Z && isDown)
540 DoSystemDebug = ScanToVirtual(thisKey) - VK_A;
541 KeInsertQueueDpc(&KbdDpc, NULL, NULL);
545 if (CurrentIrp!=NULL)
547 KEY_EVENT_RECORD* rec = (KEY_EVENT_RECORD *)
548 CurrentIrp->AssociatedIrp.SystemBuffer;
549 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(CurrentIrp);
553 rec[KeysRead].bKeyDown=isDown;
554 rec[KeysRead].wRepeatCount=1;
555 rec[KeysRead].wVirtualKeyCode=ScanToVirtual(thisKey);
556 rec[KeysRead].wVirtualScanCode=thisKey;
557 rec[KeysRead].uChar.AsciiChar=VirtualToAscii(rec->wVirtualKeyCode,isDown);
558 rec[KeysRead].dwControlKeyState=ctrlKeyState;
561 rec[KeysRead].dwControlKeyState|=ENHANCED_KEY;
564 DPRINT("KeysRequired %d KeysRead %x\n",KeysRequired,KeysRead);
565 if (KeysRead==KeysRequired)
567 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT) Context;
568 KeInsertQueueDpc(&KbdDpc,DeviceObject,CurrentIrp);
576 if (keysInBuffer==KBD_BUFFER_SIZE) // Buffer is full
581 kbdBuffer[bufHead].bKeyDown=isDown;
582 kbdBuffer[bufHead].wRepeatCount=1;
583 kbdBuffer[bufHead].wVirtualKeyCode=ScanToVirtual(thisKey);
584 kbdBuffer[bufHead].wVirtualScanCode=thisKey;
585 kbdBuffer[bufHead].uChar.UnicodeChar=0;
586 // kbdBuffer[bufHead].uChar.AsciiChar=TranslateScanCode(thisKey);
587 kbdBuffer[bufHead].uChar.AsciiChar=VirtualToAscii(kbdBuffer[bufHead].wVirtualKeyCode,isDown);
588 kbdBuffer[bufHead].dwControlKeyState=ctrlKeyState;
590 kbdBuffer[bufHead].dwControlKeyState|=ENHANCED_KEY;
592 bufHead&=KBD_WRAP_MASK; // Modulo KBD_BUFFER_SIZE
600 // Initialize keyboard
602 static void KeyboardConnectInterrupt(PDEVICE_OBJECT DeviceObject)
609 MappedIrq = HalGetInterruptVector(Internal,
615 Status = IoConnectInterrupt(&KbdInterrupt,
634 for (i = 0; i < 100; i++)
636 Status = READ_PORT_UCHAR((PUCHAR)KBD_CTRL_PORT);
637 if (!(Status & KBD_OBF))
641 (VOID)READ_PORT_UCHAR((PUCHAR)KBD_DATA_PORT);
645 static int InitializeKeyboard(PDEVICE_OBJECT DeviceObject)
647 // Initialize variables
659 KeyboardConnectInterrupt(DeviceObject);
660 KeInitializeDpc(&KbdDpc,KbdDpcRoutine,NULL);
665 * Read data from keyboard buffer
668 KbdSynchronizeRoutine(PVOID Context)
670 PIRP Irp = (PIRP)Context;
671 KEY_EVENT_RECORD* rec = (KEY_EVENT_RECORD *)Irp->AssociatedIrp.SystemBuffer;
672 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
673 ULONG NrToRead = stk->Parameters.Read.Length/sizeof(KEY_EVENT_RECORD);
676 DPRINT("NrToRead %d keysInBuffer %d\n",NrToRead,keysInBuffer);
677 NrToRead = min(NrToRead,keysInBuffer);
679 DPRINT("NrToRead %d stk->Parameters.Read.Length %d\n",
680 NrToRead,stk->Parameters.Read.Length);
681 DPRINT("sizeof(KEY_EVENT_RECORD) %d\n",sizeof(KEY_EVENT_RECORD));
682 for (i=0;i<NrToRead;i++)
684 memcpy(&rec[i],&kbdBuffer[bufTail],sizeof(KEY_EVENT_RECORD));
686 bufTail&=KBD_WRAP_MASK;
689 if ((stk->Parameters.Read.Length/sizeof(KEY_EVENT_RECORD))==NrToRead)
694 KeysRequired=stk->Parameters.Read.Length/sizeof(KEY_EVENT_RECORD);
701 VOID STDCALL KbdStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp)
704 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
707 DPRINT("KeyboardStartIo(DeviceObject %x Irp %x)\n",DeviceObject,Irp);
709 if (KeSynchronizeExecution(KbdInterrupt, KbdSynchronizeRoutine, Irp))
711 Irp->IoStatus.Status = STATUS_SUCCESS;
712 Irp->IoStatus.Information = 0;
713 IoCompleteRequest(Irp, IO_NO_INCREMENT);
714 IoStartNextPacket(DeviceObject, FALSE);
717 DPRINT("stk->Parameters.Read.Length %d\n",stk->Parameters.Read.Length);
718 DPRINT("KeysRequired %d\n",KeysRequired);
721 NTSTATUS KbdInternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
723 PIO_STACK_LOCATION stk;
724 PINTERNAL_I8042_HOOK_KEYBOARD hookKeyboard;
725 PDEVICE_EXTENSION DevExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
726 NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
728 Irp->IoStatus.Information = 0;
729 stk = IoGetCurrentIrpStackLocation(Irp);
731 switch (stk->Parameters.DeviceIoControl.IoControlCode)
733 /*-----------------11/29/2001 4:12PM----------------
734 * This internal ioctrl belongs in i8042 driver. Should be
735 * moved to the appropriate driver later.
736 * --------------------------------------------------*/
737 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
739 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(INTERNAL_I8042_HOOK_KEYBOARD))
741 DPRINT(("Keyboard IOCTL_INTERNAL_I8042_HOOK_KEYBOARD invalid buffer size\n"));
742 status = STATUS_INVALID_PARAMETER;
746 // Copy the values if they are filled in
748 hookKeyboard = (PINTERNAL_I8042_HOOK_KEYBOARD)
749 stk->Parameters.DeviceIoControl.Type3InputBuffer;
751 DevExt->HookContext = hookKeyboard->Context;
752 if (hookKeyboard->InitializationRoutine) {
753 DbgPrint("Keyboard: InitializationHookCallback NOT IMPLEMENTED\n");
754 DevExt->InitializationHookCallback =
755 hookKeyboard->InitializationRoutine;
758 if (hookKeyboard->IsrRoutine) {
759 DevExt->IsrHookCallback = hookKeyboard->IsrRoutine;
762 status = STATUS_SUCCESS;
766 status = STATUS_INVALID_DEVICE_REQUEST;
770 Irp->IoStatus.Status = status;
771 IoCompleteRequest(Irp, IO_NO_INCREMENT);
775 NTSTATUS STDCALL KbdDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
777 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp);
780 DPRINT("DeviceObject %x\n",DeviceObject);
781 DPRINT("Irp %x\n",Irp);
783 DPRINT("IRP_MJ_CREATE %d stk->MajorFunction %d\n",
784 IRP_MJ_CREATE, stk->MajorFunction);
785 DPRINT("AlreadyOpened %d\n",AlreadyOpened);
787 switch (stk->MajorFunction)
790 if (AlreadyOpened == TRUE)
793 // Status = STATUS_UNSUCCESSFUL;
794 Status = STATUS_SUCCESS;
799 Status = STATUS_SUCCESS;
800 AlreadyOpened = TRUE;
805 Status = STATUS_SUCCESS;
809 DPRINT("Handling Read request\n");
810 DPRINT("Queueing packet\n");
811 IoMarkIrpPending(Irp);
812 IoStartPacket(DeviceObject,Irp,NULL,NULL);
813 return(STATUS_PENDING);
816 Status = STATUS_NOT_IMPLEMENTED;
820 Irp->IoStatus.Status = Status;
821 Irp->IoStatus.Information = 0;
822 IoCompleteRequest(Irp,IO_NO_INCREMENT);
823 DPRINT("Status %d\n",Status);
827 NTSTATUS STDCALL DriverEntry(PDRIVER_OBJECT DriverObject,
828 PUNICODE_STRING RegistryPath)
830 * FUNCTION: Module entry point
833 PDEVICE_OBJECT DeviceObject;
834 UNICODE_STRING DeviceName = UNICODE_STRING_INITIALIZER(L"\\Device\\Keyboard");
835 UNICODE_STRING SymlinkName = UNICODE_STRING_INITIALIZER(L"\\??\\Keyboard");
837 DPRINT("Keyboard Driver 0.0.4\n");
839 DriverObject->MajorFunction[IRP_MJ_CREATE] = KbdDispatch;
840 DriverObject->MajorFunction[IRP_MJ_CLOSE] = KbdDispatch;
841 DriverObject->MajorFunction[IRP_MJ_READ] = KbdDispatch;
842 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = KbdInternalDeviceControl;
844 DriverObject->DriverStartIo = KbdStartIo;
846 IoCreateDevice(DriverObject,
847 sizeof(DEVICE_EXTENSION),
849 FILE_DEVICE_KEYBOARD,
854 RtlZeroMemory(DeviceObject->DeviceExtension, sizeof(DEVICE_EXTENSION));
856 DeviceObject->Flags = DeviceObject->Flags | DO_BUFFERED_IO;
857 InitializeKeyboard( DeviceObject );
859 IoCreateSymbolicLink(&SymlinkName, &DeviceName);
861 return(STATUS_SUCCESS);