keyboard.c

来自「一个类似windows」· C语言 代码 · 共 777 行 · 第 1/2 页

C
777
字号
				Irp->IoStatus.Status =
				              STATUS_INSUFFICIENT_RESOURCES;
				IoFreeWorkItem(WorkItem);
				goto intcontfailure;
			}
			WorkItemData->WorkItem = WorkItem;
			WorkItemData->Target =
				        DevExt->KeyboardData.ClassDeviceObject;
			WorkItemData->Irp = Irp;

			IoMarkIrpPending(Irp);
			IoQueueWorkItem(WorkItem,
			                I8042SendHookWorkItem,
					DelayedWorkQueue,
					WorkItemData);

			Irp->IoStatus.Status = STATUS_PENDING;
		}

		break;
	case IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER:
		DPRINT("IOCTL_INTERNAL_I8042_KEYBOARD_WRITE_BUFFER\n");
		if (Stk->Parameters.DeviceIoControl.InputBufferLength < 1) {
			Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
			goto intcontfailure;
		}
		if (!DevExt->KeyboardInterruptObject) {
			Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
			goto intcontfailure;
		}

		IoMarkIrpPending(Irp);
		IoStartPacket(DeviceObject, Irp, NULL, NULL);
		Irp->IoStatus.Status = STATUS_PENDING;

		break;
	case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
		DPRINT("IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n");
		if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
		                                 sizeof(KEYBOARD_ATTRIBUTES)) {
			DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_ATTRIBUTES "
			       "invalid buffer size\n");
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}
		memcpy(Irp->AssociatedIrp.SystemBuffer,
		       &DevExt->KeyboardAttributes,
		       sizeof(KEYBOARD_ATTRIBUTES));

		Irp->IoStatus.Status = STATUS_SUCCESS;
		break;
	case IOCTL_KEYBOARD_QUERY_INDICATORS:
		DPRINT("IOCTL_KEYBOARD_QUERY_INDICATORS\n");
		if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
		                       sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
			DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_INDICATORS "
			       "invalid buffer size\n");
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}
		memcpy(Irp->AssociatedIrp.SystemBuffer,
		       &DevExt->KeyboardIndicators,
		       sizeof(KEYBOARD_INDICATOR_PARAMETERS));

		Irp->IoStatus.Status = STATUS_SUCCESS;
		break;
	case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
		DPRINT("IOCTL_KEYBOARD_QUERY_TYPEMATIC\n");
		if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
		                       sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
			DPRINT("Keyboard IOCTL_KEYBOARD_QUERY_TYPEMATIC "
			       "invalid buffer size\n");
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}
		memcpy(Irp->AssociatedIrp.SystemBuffer,
		       &DevExt->KeyboardTypematic,
		       sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));

		Irp->IoStatus.Status = STATUS_SUCCESS;
		break;
	case IOCTL_KEYBOARD_SET_INDICATORS:
		DPRINT("IOCTL_KEYBOARD_SET_INDICATORS\n");
		if (Stk->Parameters.DeviceIoControl.InputBufferLength <
		                       sizeof(KEYBOARD_INDICATOR_PARAMETERS)) {
			DPRINT("Keyboard IOCTL_KEYBOARD_SET_INDICTATORS "
			       "invalid buffer size\n");
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}

		memcpy(&DevExt->KeyboardIndicators,
		       Irp->AssociatedIrp.SystemBuffer,
		       sizeof(KEYBOARD_INDICATOR_PARAMETERS));

		DPRINT("%x\n", DevExt->KeyboardIndicators.LedFlags);

		IoMarkIrpPending(Irp);
		IoStartPacket(DeviceObject, Irp, NULL, NULL);
		Irp->IoStatus.Status = STATUS_PENDING;

		break;
	case IOCTL_KEYBOARD_SET_TYPEMATIC:
		DPRINT("IOCTL_KEYBOARD_SET_TYPEMATIC\n");
		if (Stk->Parameters.DeviceIoControl.InputBufferLength <
		                       sizeof(KEYBOARD_TYPEMATIC_PARAMETERS)) {
			DPRINT("Keyboard IOCTL_KEYBOARD_SET_TYPEMATIC "
			       "invalid buffer size\n");
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}

		memcpy(&DevExt->KeyboardTypematic,
		       Irp->AssociatedIrp.SystemBuffer,
		       sizeof(KEYBOARD_TYPEMATIC_PARAMETERS));

		IoMarkIrpPending(Irp);
		IoStartPacket(DeviceObject, Irp, NULL, NULL);
		Irp->IoStatus.Status = STATUS_PENDING;

		break;
	case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
		/* We should check the UnitID, but it's kind of pointless as
		 * all keyboards are supposed to have the same one
		 */
		if (Stk->Parameters.DeviceIoControl.OutputBufferLength <
		                 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) {
			DPRINT("IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: "
			       "invalid buffer size (expected)\n");
			/* It's to query the buffer size */
			Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
			goto intcontfailure;
		}
		Irp->IoStatus.Information =
		                 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION);

		memcpy(Irp->AssociatedIrp.SystemBuffer,
		       &IndicatorTranslation,
		       sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION));

		Irp->IoStatus.Status = STATUS_SUCCESS;
		break;
	case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD:
		/* Nothing to do here */
		Irp->IoStatus.Status = STATUS_SUCCESS;
		break;
	default:
		Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
		break;
	}

intcontfailure:
	return Irp->IoStatus.Status;
}

/* This is all pretty confusing. There's more than one way to
 * disable/enable the keyboard. You can send KBD_ENABLE to the
 * keyboard, and it will start scanning keys. Sending KBD_DISABLE
 * will disable the key scanning but also reset the parameters to
 * defaults.
 *
 * You can also send 0xAE to the controller for enabling the
 * keyboard clock line and 0xAD for disabling it. Then it'll
 * automatically get turned on at the next command. The last
 * way is by modifying the bit that drives the clock line in the
 * 'command byte' of the controller. This is almost, but not quite,
 * the same as the AE/AD thing. The difference can be used to detect
 * some really old broken keyboard controllers which I hope won't be
 * necessary.
 *
 * We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse
 * some kvm switches.
 */

BOOLEAN STDCALL I8042KeyboardEnable(PDEVICE_EXTENSION DevExt)
{
	UCHAR Value;
	NTSTATUS Status;

	DPRINT("Enable keyboard\n");

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
		DPRINT1("Can't read i8042 mode\n");
		return FALSE;
	}

	Status = I8042ReadDataWait(DevExt, &Value);
	if (!NT_SUCCESS(Status)) {
		DPRINT1("No response after read i8042 mode\n");
		return FALSE;
	}

	Value &= ~CCB_KBD_DISAB; // don't disable keyboard

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
		DPRINT1("Can't set i8042 mode\n");
		return FALSE;
	}

	if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
		DPRINT1("Can't send i8042 mode\n");
		return FALSE;
	}

	return TRUE;
}

static BOOLEAN STDCALL I8042KeyboardDefaultsAndDisable(PDEVICE_EXTENSION DevExt)
{
	UCHAR Value;
	NTSTATUS Status;

	DPRINT("Disabling keyboard\n");

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
		DPRINT1("Can't read i8042 mode\n");
		return FALSE;
	}

	Status = I8042ReadDataWait(DevExt, &Value);
	if (!NT_SUCCESS(Status)) {
		DPRINT1("No response after read i8042 mode\n");
		return FALSE;
	}

	Value |= CCB_KBD_DISAB; // disable keyboard

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
		DPRINT1("Can't set i8042 mode\n");
		return FALSE;
	}

	if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
		DPRINT1("Can't send i8042 mode\n");
		return FALSE;
	}

	return TRUE;
}

BOOLEAN STDCALL I8042KeyboardEnableInterrupt(PDEVICE_EXTENSION DevExt)
{
	UCHAR Value;
	NTSTATUS Status;

	DPRINT("Enabling keyboard interrupt\n");

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
		DPRINT1("Can't read i8042 mode\n");
		return FALSE;
	}

	Status = I8042ReadDataWait(DevExt, &Value);
	if (!NT_SUCCESS(Status)) {
		DPRINT1("No response after read i8042 mode\n");
		return FALSE;
	}

	Value &= ~CCB_KBD_DISAB; // don't disable keyboard
	Value |= CCB_KBD_INT_ENAB;    // enable keyboard interrupts

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
		DPRINT1("Can't set i8042 mode\n");
		return FALSE;
	}

	if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
		DPRINT1("Can't send i8042 mode\n");
		return FALSE;
	}

	return TRUE;
}

BOOLEAN STDCALL I8042DetectKeyboard(PDEVICE_EXTENSION DevExt)
{
	NTSTATUS Status;
	UCHAR Value;
	UCHAR Value2;
	ULONG RetryCount = 10;

	DPRINT("Detecting keyboard\n");

	I8042KeyboardDefaultsAndDisable(DevExt);

	do {
		I8042Flush();
		Status = I8042SynchWritePort(DevExt, 0, KBD_GET_ID, TRUE);
	} while (STATUS_IO_TIMEOUT == Status && RetryCount--);

	if (!NT_SUCCESS(Status)) {
		DPRINT1("Can't write GET_ID (%x)\n", Status);
		/* Could be an AT keyboard */
		DevExt->KeyboardIsAT = TRUE;
		goto detectsetleds;
	}

	Status = I8042ReadDataWait(DevExt, &Value);
	if (!NT_SUCCESS(Status)) {
		DPRINT1("No response after GET_ID\n");
		/* Could be an AT keyboard */
		DevExt->KeyboardIsAT = TRUE;
		goto detectsetleds;
	}
	DevExt->KeyboardIsAT = FALSE;

	if (Value != 0xAB && Value != 0xAC) {
		DPRINT("Bad ID: %x\n", Value);
		/* This is certainly not a keyboard */
		return FALSE;
	}

	Status = I8042ReadDataWait(DevExt, &Value2);
	if (!NT_SUCCESS(Status)) {
		DPRINT("Partial ID\n");
		return FALSE;
	}

	DPRINT("Keyboard ID: 0x%x 0x%x\n", Value, Value2);

detectsetleds:
	I8042Flush(); /* Flush any bytes left over from GET_ID */

	Status = I8042SynchWritePort(DevExt, 0, KBD_SET_LEDS, TRUE);
	if (!NT_SUCCESS(Status)) {
		DPRINT("Can't write SET_LEDS (%x)\n", Status);
		return FALSE;
	}
	Status = I8042SynchWritePort(DevExt, 0, 0, TRUE);
	if (!NT_SUCCESS(Status)) {
		DPRINT("Can't finish SET_LEDS (%x)\n", Status);
		return FALSE;
	}

	// Turn on translation

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_READ_MODE)) {
		DPRINT1("Can't read i8042 mode\n");
		return FALSE;
	}

	Status = I8042ReadDataWait(DevExt, &Value);
	if (!NT_SUCCESS(Status)) {
		DPRINT1("No response after read i8042 mode\n");
		return FALSE;
	}

	Value |= 0x40;    // enable keyboard translation

	if (!I8042Write(DevExt, I8042_CTRL_PORT, KBD_WRITE_MODE)) {
		DPRINT1("Can't set i8042 mode\n");
		return FALSE;
	}

	if (!I8042Write(DevExt, I8042_DATA_PORT, Value)) {
		DPRINT1("Can't send i8042 mode\n");
		return FALSE;
	}

	return TRUE;
}

/* debug stuff */
VOID STDCALL
KdpServiceDispatcher(ULONG Code, PVOID Context1, PVOID Context2);
#define EnterDebugger ((PVOID)0x25)

static VOID STDCALL I8042DebugWorkItem(PDEVICE_OBJECT DeviceObject,
                                       PVOID Context)
{
	ULONG Key;
	PFDO_DEVICE_EXTENSION FdoDevExt = DeviceObject->DeviceExtension;
	PDEVICE_EXTENSION DevExt = FdoDevExt->PortDevExt;

	Key = InterlockedExchange((PLONG)&DevExt->DebugKey, 0);
	DPRINT("Debug key: %x\n", Key);

	if (!Key)
		return;

#ifdef __REACTOS__
	/* We hope kernel would understand this. If
	 * that's not the case, nothing would happen.
	 */
	KdpServiceDispatcher(TAG('R', 'o', 's', ' '), (PVOID)Key, NULL);
#endif /* __REACTOS__ */
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?