键盘驱动程序是一个最基本的驱动程序,通过学习这个简单而实用的驱动程序来了解ReactOS驱动程序的结构,驱动程序的运行流程。在前面的I/O管理器分析里,已经知道操作系统加载驱动程序的过程,也了解了驱动程序在系统里是以驱动程序对象来管理的,也就是通过DRIVER_OBJECT对象来表示一个驱动程序的。
键盘驱动程序是一个输入输出的设备,但在ReactOS驱动程序分类里,是分在输入设备类。这个驱动程序的源程序所在的目录是在reactos/drivers/input/i8042prt目录。在分析这个驱动程序,还是采用从程序运行过程来分析它的流程。那么驱动程序加载运行的入口点在那里呢?这是一个最先要解决的问题,其实每个驱动程序都固定有一个函数名称DriverEntry,因此操作系统加载驱动程序时,就会找到这个入口函数,然后就调这个函数,就可以把驱动程序和操作系统内核联系在一起了,就可以调用驱动程序相关的功能了。在内核的I/O管理器里是这样调用这个函数的,如下:
#148 DPRINT("Calling driver entrypoint at %p/n", InitializationFunction);
#149 Status = (*InitializationFunction)(DriverObject, RegistryPath);
通过内核的调用,就知道函数DriverEntry应具备两个参数,第一个参数是驱动程序对象,这是内核表示一个驱动程序的对象。第二个参数是驱动程序文件在注册表里的路径。因此,驱动程序入口函数,就写成下面这样:
#001 NTSTATUS NTAPI
#002 DriverEntry(
#003 IN PDRIVER_OBJECT DriverObject,
#004 IN PUNICODE_STRING RegistryPath)
#005 {
#006 PI8042_DRIVER_EXTENSION DriverExtension;
#007 ULONG i;
#008 NTSTATUS Status;
#009
#010 /* ROS Hack: ideally, we shouldn't have to initialize debug level this way,
#011 but since the only way is to change it via KDBG, it's better to leave
#012 it here too. */
#013 #if 0
#014 DbgSetDebugFilterState(
#015 DPFLTR_I8042PRT_ID,
#016 (1 << DPFLTR_ERROR_LEVEL) | (1 << DPFLTR_WARNING_LEVEL) |
#017 (1 << DPFLTR_TRACE_LEVEL) /*| (1 << DPFLTR_INFO_LEVEL)*/ | DPFLTR_MASK,
#018 TRUE);
#019 #endif
#020
#021
调用函数IoAllocateDriverObjectExtension来分配键盘驱动程序的对象内存,以便保存更多扩展的属性。
#022 Status = IoAllocateDriverObjectExtension(
#023 DriverObject,
#024 DriverObject,
#025 sizeof(I8042_DRIVER_EXTENSION),
#026 (PVOID*)&DriverExtension);
#027 if (!NT_SUCCESS(Status))
#028 {
#029 WARN_(I8042PRT, "IoAllocateDriverObjectExtension() failed with status 0x%08lx/n", Status);
#030 return Status;
#031 }
初始化扩展分区内存。
#032 RtlZeroMemory(DriverExtension, sizeof(I8042_DRIVER_EXTENSION));
初始化扩展的设备列表。
#033 KeInitializeSpinLock(&DriverExtension->Port.SpinLock);
#034 InitializeListHead(&DriverExtension->DeviceListHead);
#035 KeInitializeSpinLock(&DriverExtension->DeviceListLock);
#036
拷贝注册表路径。
#037 Status = DuplicateUnicodeString(
#038 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
#039 RegistryPath,
#040 &DriverExtension->RegistryPath);
#041 if (!NT_SUCCESS(Status))
#042 {
#043 WARN_(I8042PRT, "DuplicateUnicodeString() failed with status 0x%08lx/n", Status);
#044 return Status;
#045 }
#046
从注册表里获取驱动程序的资源分配。
#047 Status = ReadRegistryEntries(RegistryPath, &DriverExtension->Port.Settings);
#048 if (!NT_SUCCESS(Status))
#049 {
#050 WARN_(I8042PRT, "ReadRegistryEntries() failed with status 0x%08lx/n", Status);
#051 return Status;
#052 }
#053
添加即插即用调用函数,以便创建这个驱动程序支持的设备。
#054 DriverObject->DriverExtension->AddDevice = i8042AddDevice;
指向驱动程序中处理串行I/O请求的函数,I/O管理器自动为驱动程序串行化多个I/O请求。
#055 DriverObject->DriverStartIo = i8042StartIo;
#056
缺省地初始化IRP消息为IrpStub函数处理。
#057 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
#058 DriverObject->MajorFunction[i] = IrpStub;
#059
指定IRP消息IRP_MJ_CREATE的处理函数i8042Create。
#060 DriverObject->MajorFunction[IRP_MJ_CREATE] = i8042Create;
清除分配资源的函数i8042Cleanup。
#061 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = i8042Cleanup;
指定关闭设备时,调用的函数i8042Close。
#062 DriverObject->MajorFunction[IRP_MJ_CLOSE] = i8042Close;
通过IO操作函数i8042DeviceControl。
#063 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = i8042DeviceControl;
指定中断处理函数i8042InternalDeviceControl。
#064 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = i8042InternalDeviceControl;
指定即插即用时响应函数i8042Pnp。
#065 DriverObject->MajorFunction[IRP_MJ_PNP] = i8042Pnp;
#066
判断驱动程序是否初始化安装时运行,如果是初始化时就调用函数i8042AddLegacyKeyboard处理。
#067 if (IsFirstStageSetup())
#068 return i8042AddLegacyKeyboard(DriverObject, RegistryPath);
#069
#070 return STATUS_SUCCESS;
#071 }