Windows驱动开发入门-串口过滤驱动 设备绑定 IoAttachDevice 把一个物理设备与一个设备对象绑定。
1 2 3 4 5 6 7 8 NTSTATUS IoAttachDevice ( IN PDEIVE_OBJECT SourceDevice, IN PUNICODE_STRING TargetDevice, OUT PDEVICE_OBJECT *AttachedDevice )
IoAttachDeviceToDeviceStackSafe 上面那个API没法绑定没名字的设备,这个根据设备对象指针绑定。
1 2 3 4 5 6 7 8 NTSTATUS IoAttachDeviceToDeviceStackSafe ( IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice, IN OUT PDEVICE_OBJECT* AttachedToDeviceObject )
IoAttachDeviceToDeviceStack 上面那个的不安全版本,但是就用这个了。
1 2 3 4 PDEVICE_OBJECT IoAttachDeviceToDeviceStackSafe ( IN PDEVICE_OBJECT SourceDevice, IN PDEVICE_OBJECT TargetDevice )
获取设备 IoCreateDevice 生成过滤设备。
1 2 3 4 5 6 7 8 9 10 11 12 NTSTATUS IoCreateDevice ( IN PDRIVER_OBJECT DriverObject, IN ULONG DeviceExtensionSize, IN OPTIONAL PUNICODE_STRING DeviceName, IN DEVICE_TYPE DeviceType, IN ULONG DeviceCharacteristics )
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 NTSTATUS ccpAttachDevice (PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT* fltobj, PDEVICE_OBJECT* next) { NTSTATUS status=STATUS_SUCCESS; PDEVICE_OBJECT topdev = NULL ; status = IoCreateDevice (driver, 0 , NULL , oldobj->DeviceType, 0 , FALSE, fltobj); if (status != STATUS_SUCCESS) return status; if (oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if (oldobj->Flags & DO_DIRECT_IO) (*fltobj)->Flags |= DO_DIRECT_IO; if (oldobj->Flags & DO_BUFFERED_IO) (*fltobj)->Flags |= DO_BUFFERED_IO; if (oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN) (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN; (*fltobj)->Flags |= DO_POWER_PAGABLE; topdev = IoAttachDeviceToDeviceStack (*fltobj, oldobj); if (topdev == NULL ) { IoDeleteDevice (*fltobj); *fltobj = NULL ; status = STATUS_UNSUCCESSFUL; return status; }; *next = topdev; (*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; };
IoGetDeviceObjectPointer 从名字获得设备对象指针。
1 2 3 4 5 6 7 8 9 10 NTSTATUS IoGetDeviceObjectPointer ( IN PUNICODE_STRING ObjectName, IN ACCESS_MASK DesiredAccess, OUT PFILE_OBJECT* FileObject, OUT PDEVICE_OBJECT* DeviceObject )
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 PDEVICE_OBJECT ccpOpenCom (ULONG id, PNTSTATUS status) { UNICODE_STRING name_str = { 0 }; static WCHAR name[32 ] = { 0 }; PFILE_OBJECT fileobj = NULL ; PDEVICE_OBJECT devobj = NULL ; memset (name, 0 , sizeof (WCHAR) * 32 ); RtlStringCchPrintfW (name, 32 , TEXT ("\\Device\\Serial%d" ), id); RtlInitUnicodeString (&name_str, name); *status = IoGetDeviceObjectPointer (&name_str, FILE_ALL_ACCESS, &fileobj, &devobj); if (*status == STATUS_SUCCESS) ObDereferenceObject (fileobj); return devobj; };
绑定所有串口 很简单,假设计算机串口最多32个,然后挨个试,能成功绑定的就绑定,绑定不了的就算了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #define CCP_MAX_COM_ID 32 static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 };static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 };VOID ccpAttachAllComs (PDRIVER_OBJECT driver) { ULONG i = 0 ; PDEVICE_OBJECT com_ob = { 0 }; NTSTATUS status = STATUS_SUCCESS; for (i = 0 ; i < CCP_MAX_COM_ID; i++) { com_ob = ccpOpenCom (i, &status); if (com_ob == NULL ) continue ; ccpAttachDevice (driver, com_ob, &s_fltobj[i], &s_nextobj[i]); }; return ; };
请求 通过请求的主功能号进行分类,读请求主功能号为IRP_MJ_READ,写请求主功能号为IRP_MJ_WRITE。
当过滤驱动不做任何事儿,直接放行请求到底层设备时,使用IoSkipCurrentIrpStackLocation
跳过当前栈空间,并用IoCallDriver
把这个请求发送给底层设备。
关于如何获取串口写请求所发送来的数据,IRP结构中有三处缓冲区结构,反正这仨都读一遍就行,哪个能读到就是哪个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 NTSTATUS ccpDispatch (PDEVICE_OBJECT device, PIRP irp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation (irp); NTSTATUS status=STATUS_SUCCESS; ULONG i=0 , j=0 ; for (i = 0 ; i < CCP_MAX_COM_ID; i++) if (s_fltobj[i] == device) { if (irpsp->MajorFunction == IRP_MJ_POWER) { PoStartNextPowerIrp (irp); IoSkipCurrentIrpStackLocation (irp); return PoCallDriver (s_nextobj[i], irp); }; if (irpsp->MajorFunction == IRP_MJ_WRITE) { ULONG len = irpsp->Parameters.Write.Length; PUCHAR buf = NULL ; if (irp->MdlAddress != NULL ) buf = (PUCHAR)MmGetSystemAddressForMdlSafe (irp->MdlAddress, NormalPagePriority); else buf = (PUCHAR)irp->UserBuffer; if (buf == NULL ) buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer; for (j = 0 ; j < len; ++j) DbgPrint ("comcap: Send Data: %2x\r\n" , buf[j]); }; IoSkipCurrentIrpStackLocation (irp); return IoCallDriver (s_nextobj[i], irp); }; irp->IoStatus.Information = 0 ; irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest (irp, IO_NO_INCREMENT); return STATUS_SUCCESS; };
动态卸载 一定要解除绑定,要不一卸载就蓝屏。IoDetachDevice
将绑定的设备解绑,IoDeleteDevice
删除设备以回收内存,KeDelayExecutionThread
纯粹负责延时。卸载时可能还有些IRP正在被处理,取消非常麻烦还不一定成功,最好的方法就解绑设备后是等五秒,处理后再删除设备。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) #define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000) VOID ccpUnload (PDRIVER_OBJECT drv) { ULONG i=0 ; LARGE_INTEGER interval = { 0 }; for (i = 0 ; i < CCP_MAX_COM_ID; i++) if (s_nextobj[i] != NULL ) IoDetachDevice (s_nextobj[i]); interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND); KeDelayExecutionThread (KernelMode, FALSE, &interval); for (i = 0 ; i < CCP_MAX_COM_ID; i++) if (s_fltobj[i] != NULL ) IoDeleteDevice (s_fltobj[i]); return ; };
驱动设备入口 1 2 3 4 5 6 7 8 9 10 11 12 NTSTATUS DriverEntry (PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { size_t i=0 ; for (i = 0 ; i < IRP_MJ_MAXIMUM_FUNCTION; i++) driver->MajorFunction[i] = ccpDispatch; driver->DriverUnload = ccpUnload; ccpAttachAllComs (driver); return STATUS_SUCCESS; };