<span style="font-size:14px;">typedef struct _BEEP_DEVICE_EXTENSION { LONG ReferenceCount; FAST_MUTEX Mutex; KTIMER Timer; LONG TimerActive; PVOID SectionHandle; } DEVICE_EXTENSION, *PDEVICE_EXTENSION;</span>
上面是蜂鸣器设备的扩展结构体,其中ReferenceCount是设备的引用计数,而FAST_MUTEX用于互斥访问这个计数器,Timer用于蜂鸣器定时,而TimerActive则计数多少个定时器被激活。SectionHandle则保存整个系统的内存句柄。
根据惯例,驱动程序的入口处有一个创建设备的过程,不过在一般的创建之后需要额外加上一个对设备扩展体的初始化:
DeviceExtension->ReferenceCount = 0; DeviceExtension->TimerActive = FALSE; IoInitializeDpcRequest(DeviceObject, (PIO_DPC_ROUTINE)BeepDPC); KeInitializeTimer(&DeviceExtension->Timer); ExInitializeFastMutex(&DeviceExtension->Mutex); MmPageEntireDriver(DriverEntry);
最后一个函数将整个内存给换页出去,因为创建设备之后整个设备并不一定是需要的。所以可以暂时将这段代码给换页出去,当然这里的DriverEntry是指一个内存地址——函数本身具备地址的含义(具体可以参考之前的博客http://blog.csdn.net/dayenglish/article/details/21345093)。
当然,既然在初始化整个设备和驱动程序的时候讲内存给换页出去了,那么就需要在某一个阶段将整个驱动锁定到内存当中——也就是讲内存给设置为非换页内存。而一个设备的访问起点往往是CreateXxx调用,当控制权转移到驱动程序的时候,相对应的是IRP_MJ_CREATE控制处理函数。创建过程很简单,直接通过快速互斥体将整个设备的计数给保护起来,然后将驱动代码给锁定到非换页内存当中,最后返回成功。
ExAcquireFastMutex(&DeviceExtension->Mutex); if (++DeviceExtension->ReferenceCount == 1) { DeviceExtension->SectionHandle = MmLockPagableDataSection(BeepCreate); } ExReleaseFastMutex(&DeviceExtension->Mutex); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;
因为蜂鸣器是一个比较简单的设备,所以没有过多的读写操作。所有的控制都发送给IRP_MJ_DEVICE_CONTROL控制码处理函数。这里的处理仅仅是做一些参数检验,如果蜂鸣器的频率和持续时间均为0,那么返回STATUS_PENDING,并且将这个IRP放入到队列当中处理。否则。直接调用IoCompleteRequest例程完成IO请求。
Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; if (Status == STATUS_PENDING) { IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, BeepCancel); } else { IoCompleteRequest(Irp, IO_NO_INCREMENT); }
当控制权从IoStartPacket转入到驱动程序的SatrtIo函数的时候,表明整个系统已经开始处理相应的IRP请求了,所以第一步就是防止相应的IRP请求被取消。如果定时器还没有激发,就将定时器给取消。否则直接发出一个蜂鸣器响应,然后根据延迟时间设置定时器在定时器超时的时候进行蜂鸣器响应。当然,系统超时使用了DPC例程,当所有的处理都结束的时候,系统处理排队当中的下一个IO请求。并且返回成功。
IoAcquireCancelSpinLock(&CancelIrql); if (!Irp) { IoReleaseCancelSpinLock(CancelIrql); return; } (VOID)IoSetCancelRoutine(Irp, NULL); IoReleaseCancelSpinLock(CancelIrql); BeepParam = (PBEEP_SET_PARAMETERS)Irp->AssociatedIrp.SystemBuffer; IoStack = IoGetCurrentIrpStackLocation(Irp); if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_BEEP_SET) { if (DeviceExtension->TimerActive) { if (KeCancelTimer(&DeviceExtension->Timer)) { InterlockedDecrement(&DeviceExtension->TimerActive); } } if (HalMakeBeep(BeepParam->Frequency)) { Status = STATUS_SUCCESS; DueTime.QuadPart = BeepParam->Duration * -10000; InterlockedIncrement(&DeviceExtension->TimerActive); KeSetTimer(&DeviceExtension->Timer, DueTime, &DeviceObject->Dpc); } }
由于上面试分析IO请求没有被取消的过程,如果IO请求被取消的时候,系统会转向调用在IoStartPacket函数当中设置的取消例程,也就是BeepCancel例程,这个历程的执行分为两步,第一步检验,正在被处理的是否就是将要被取消的IRP,如果是则直接调用处理下一个IRP。否则将需要取消的IRQP从队列当中移除。
if (Irp == DeviceObject->CurrentIrp) { DeviceObject->CurrentIrp = NULL; IoReleaseCancelSpinLock(Irp->CancelIrql); IoStartNextPacket(DeviceObject, TRUE); } else { KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry); IoReleaseCancelSpinLock(Irp->CancelIrql); }
而作为定时器的DPC处理过程也很简单,仅仅发出蜂鸣,然后将定时器技术递减1。
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; HalMakeBeep(0); InterlockedDecrement(&DeviceExtension->TimerActive);蜂鸣器的关闭操作和创建是一个相对的反过程,还有一个是蜂鸣器的卸载。蜂鸣器的卸载主要是看定时器操作有没有结束,如果没有结束,则强制将定时器给取消,然后调用IoDeleteDevice删除设备对象。较为复杂的是一个蜂鸣器的清理例程,蜂鸣器通过设备的当前 IRP循环整个 IRP队列,将所有的 IRP都处理掉,当然返回值肯定不能是 STATUS_SUCCESS,而应该是 STATUS_CANCELLED,因为并没有实质性的蜂鸣处理。
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); IoAcquireCancelSpinLock(&CancelIrql); CurrentIrp = DeviceObject->CurrentIrp; DeviceObject->CurrentIrp = NULL; while (CurrentIrp) { (VOID)IoSetCancelRoutine(CurrentIrp, NULL); CurrentIrp->IoStatus.Status = STATUS_CANCELLED; CurrentIrp->IoStatus.Information = 0; IoReleaseCancelSpinLock(CancelIrql); IoCompleteRequest(CurrentIrp, IO_NO_INCREMENT); IoAcquireCancelSpinLock(&CancelIrql); Packet = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue); if (Packet) { CurrentIrp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.DeviceQueueEntry); } else { CurrentIrp = NULL; } } IoReleaseCancelSpinLock(CancelIrql); KeLowerIrql(OldIrql);完整的代码参见资源页面