这是前段时间做驱动时碰到的一些问题,总结了下,供以后参考。
1、调试时当断点找不到相对应的源文件时,
1、在DriverEntry开头中加入_asm{int 3};即可
2、在windbg中设置符号文件、源文件和镜像文件的路径
3、注意inf文件中的名字和sys文件名是否一致(这个不太清楚)
4、注意系统加载的驱动是否就是我们自己的驱动,最好每次加载新的驱动时,删除所有以前的驱动,确保系统 找不到,弹出手动加载驱动。
备注:当原因是第四点时,很有可能会出现蓝屏的情况,下午再试试。
2、返回状态值在wdk中的ntstatus.h中查找。
3、#define STOP_HERE(void) /
_asm{int 3};
4、当卸载驱动时,先触发一个IRP_MN_QUERY_REMOVE_DEVICE请求,暂停设备堆栈中所有的I/O请求并更新PNP的状态,使用一个循环来完成这个处理;再这个请求处理完成后又产生一个IRP_MN_REMOVE_DEVICE请求,在这个请求处理里面完成对设备资源的删除,(请求队列清空、设备堆栈的删除、电源管理、设备对象、设备接口、符号链接等等);最后进入UNLOAD函数,其实经过了IRP_MN_QUERY_REMOVE_DEVICE和IRP_MN_REMOVE_DEVICE处理,这个里面就完成了对全局变量中使用到内存的地方进行安全释放。备注:如果在IRP_MN_REMOVE_DEVICE中不删除符号链接,则在你执行完卸载操作后再为设备安装驱动时系统会蓝屏,信息为
IRP_CANCEL_WITHOUT_UNCOMPLETED_OPERATION,大概是这么写的,意思是为取消之前还有没有做完的操作。
5、读写card配置信息,在DS生成的框架中提供了获得配置空间信息的函数XXXAccessConfigSpace,具体用法如下:
PCI_COMMON_CONFIG pci_config = {0};
ULONG ReadWrittenSize = 0;
status = HK6802VAccessConfigSpace(&deviceExtension, TRUE, &pci_config, 0, sizeof(PCI_COMMON_CONFIG),&ReadWrittenSize);
6、关于配置空间。配置空间在系统启动时就已经配置好了,我们所要做的只是在AddDevice中根据配置空间的设置来对I/O端口、Memory等等进行初始化。当然每次启动时都会进行这个操作的,可以用windbg跟踪下系统启动的过程,在AddDevice中打上断点。
7、BUFFER NONE 与 BUFFER QUEUE的比较从函数结构上看DVR6805_NONEReadDispatch步骤:1、检查请求是否可以继续,主要是通过检查DVR6805_NONE_IO_LOCK 这个结构,如果不可以或者挂起就返回status值;2、获得当前IRP堆栈信息和读缓冲区长度;3、获得缓冲区区首地址并且立即执行IoCompleteRequest (Irp, IO_NO_INCREMENT),并且设置自旋锁保证处理过程不被外部干扰KeAcquireSpinLock(&IoLock->IoLock, &oldIrql);4、处理完毕释放自旋锁KeReleaseSpinLock(&IoLock->IoLock, oldIrql)。
DVR6805ReadDispatch步骤:1、将读请求IRP直接放入读队列DVR6805QueueIrp;2、如果当前队列中没有请求,则立即跳转到相应的StartIo回调函数DVR6805ReadQueueStartIo中执行。该回调函数和队列的初始化是在AddDevice中完成的DVR6805InitializeQueue(
&deviceExtension->ReadQueue,
DVR6805ReadQueueStartIo,
deviceExtension->DeviceObject,
TRUE
);
8、应用层异步读、驱动层队列处理IRP
在生成的代码中,当驱动层使用队列方式来处理读的IRP时,应用层如果使用同步读的话,将不会调到IRP_MJ_READ对应的派遣函数,也就是不会响应读这个动作,ReadFile这个操作返回0,错误码为87,代表参数错误,个人理解为应用层和驱动层读方式不一致导致的。
当应用层使用异步事件来读设备,程序中如果队列空闲,则立即处理当前IRP,如果队列繁忙,则插入队列尾部并返回状态值PENDING,错误码为103。在程序中,由于调试队列繁忙不容易或者因为我没有好的方法,我就在程序处理完成第一个IRP代码之后将Queue->StallCount = 1(改变过队列的irp状态,在检查队列状态之前Queue->CurrentIrp = irp,结果蓝屏IRQL_NOT_LESS_OR_EQUAL,意思是在高级别中使用了分页内存,估计是这个赋值动作完成在分页内存中,但是在判断后首先调用IoMarkIrpPending(Irp),紧接着就Queue->CurrentIrp = irp,这里不解!),表示队列中仍然存在一个IRP请求,其实是为空的。这样,当应用层循环读的时候,第一次OK,第二次进来时检查队列时发现队列IRP数为1,说明队列繁忙,这时会执行插入队列的操作。当然除了第一次,后面的均阻塞了,队列头部的假信息导致之后的IRP都不能得到处理,应用层WaitForSingleObject等待事件结束也一直被阻塞,应用程序陷入无限等待。
9、中断处理
在Isr中要做的几件事
(1)判断是否掉电,如果掉电了就啥都不用做了,返回FALSE。
if ( PowerDeviceD0 != deviceExtension->DevicePowerState )
{
return FALSE;
}
(2)读卡寄存器,看看是否是我们的卡来了中断
IntStatus = READ_REGISTER_ULONG((PULONG)(deviceExtension->MemoryBase + INTSTAT));
if (!IntStatus)
{
return FALSE;
}
(3)没有irp请求,就是我们的卡来了中断,也不用理睬
if (!deviceExtension->QueueArray[0]->CurrentIrp)
{
WRITE_REGISTER_ULONG((PULONG)(deviceExtension->MemoryBase + INTSTAT), 0xffffffff);
return TRUE;
}
(4)我们的卡产生了中断并且也传来了一个待处理的IRP,接下来的操作就是清掉中断位和进入DPC处理
//将中断清掉,以便下一次能够响应该中断
WRITE_REGISTER_ULONG((PULONG)(deviceExtension->MemoryBase + INTSTAT), 0xffffffff);
//调用DPC例程处理剩下的操作
KeInsertQueueDpc(&deviceExtension->GHIsrDpc, NULL, NULL);
10、ReadFile、WriteFile操作(下以ReadFile后驱动操作为例)、如
果要进队列,这里的操作就放在StartIo中做就可以了。
NTSTATUS GHReadDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PGH_DEVICE_EXTENSION deviceExtension;
NTSTATUS status;
PIO_STACK_LOCATION irpStack;
ULONG Loffset;
ULONG Length;
GHDebugPrint(DBG_IO, DBG_TRACE, __FUNCTION__"++. IRP %p", Irp);
deviceExtension = (PGH_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
//偏移量
Loffset = irpStack->Parameters.Read.ByteOffset.QuadPart;
//长度
Length = irpStack->Parameters.Read.Length;
//模拟缓冲区内容
memset((PUCHAR)deviceExtension->vaCommonBuffer, 0x99, 16 * PAGE_SIZE);
RtlCopyMemory((PUCHAR)Irp->AssociatedIrp.SystemBuffer + Loffset, (PUCHAR)deviceExtension->vaCommonBuffer, Length);
// put the read IRP in our read queue
// status = GHQueueIrp(&deviceExtension->ReadQueue, Irp);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = Length;
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
GHDebugPrint(DBG_IO, DBG_TRACE, __FUNCTION__"--. IRP %p STATUS %x", Irp, status);
return status;
}
11、FIFO调试
在6804中,我们设置VDAMTRIG为0x17,VDMAPEN为1,VFIFOEN为1,然后开FIFO溢出中断。接上摄像头之后,立马就响应了VFFOF(FIFO溢出)这个中断。我们可以发现,这个中断一直都在响应(就算你清掉了),原因是FIFO溢出后并没有将里面的数据搬移出来,会不停的触发这个中断,响应一段时间后蓝屏,提示陷入一个死循环了。dd watchdog!g_WdBugCheckData l5 ?查下这个命令 dds
12、DMA调试
昨晚出图像了,呵呵。令人高兴的是提前了一个月完成第一阶段目标,不过在解决问题时发现,导致最严重后果的并不是最关键的问题,反而是最易疏忽的细节,如果考虑问题再全面一点,大胆地去怀疑每一个步骤和细节,比起创造力更加重要。回头看看DMA传输这一块,大家都觉得很简单了,初始化、设置指令、开始传输、读取数据,流程很清晰。下面就这些步骤中出现的一些问题和一些考虑做一下总结。
(1) 初始化
初始化的工作:DMA、FIFO去使能(也是清空FIFO的步骤),清除指令寄存器,关FIFO、DMA中断。这部分没有什么大的问题。
(2) 设置指令(我们调试时碰的最多的部分)
首先是对指令的不熟悉,当时看DATASHEET并不清楚需要用到哪些指令,像同步、LINESTART、JUMP一无所知。最刚开始的想法是写一条LINESTART执行,看能不能读点什么东西出来,很显然是没有的。因为并不知道指令的格式是怎样组织的、指令中涉及到得地址是虚拟地址还是物理地址,为这个问题都搞了一天(当你的地址是给硬件用的时候肯定是物理地址,反之亦然)。确定物理地址后,每次取多少就是我们最关心的地方了,根据N制的标准,一行为720,那么我们一条LINESTART是否可以取720个点呢(与RGB格式有关,为RGB16时,一行为720 * 2点,为RGB24时,一行为720 * 3点)?当时并不知道,只能一步一步试。当把每行与RGB格式结合起来考虑还是过了蛮长的时间~~。
指令的对与错
当出现了指令错误中断时,第一反应这指令错了,但在查看DMA_EXE和DMA_PP时并不是指向那条错误的指令。指令错了DMA会停止吗?抑或继续?后来为了测试,加入JUMP后形成循环,指令错误消失,说明设置的指令并没有问题,而是没有JUMP后DMA一直在执行,而后面的内存并没有指令,当然出错。
数据取出来之后,应用层也犯了些低级错误,在行和列的处理上写错了
for (int i=0; i<HEIGTH; i++)
{
for (int j=0; j<WIDTH; j++)
{
UCHAR R = buff[i*WIDTH*COLORFORMAT + j*COLORFORMAT + 2];
UCHAR G = buff[i*WIDTH*COLORFORMAT + j*COLORFORMAT + 1];
UCHAR B = buff[i*WIDTH*COLORFORMAT + j*COLORFORMAT + 0];
pDc->SetPixel(100 + j, 100 + i, RGB(R, G, B));
}
}
之前是将i * HEIGTH了,第一次写对了,第二次写错了,过了好久检查问题时才发现,悲剧中。。。。数据显示当然是有问题的,同步还没加。
关于隔行的问题,当初考虑到隔行扫描的问题,以为里面数据已经是隔行的了,现在看来并非如此,这个设计是放在驱动中完成的,现在我们的驱动取出来的数据就是隔行的
//创建奇场 LINESTART
while( CmdIndex < CMD_NUM )
{
CrtOneDmaCmd(&cmd_co[CmdIndex], CMD_TYPE_LINESTART, Irq, DataType, StartingByte, ByteCount, O_Starting);
CO_Starting += ByteCount * 2;//数据隔行
CmdIndex++;
}
你认为CO_Starting += ByteCount * 2是隔行呢还是CO_Starting += ByteCount隔行,越是细节问题越容易一闪而过,好的执行力更重要,细节远比创造力更有价值。
13、在中断调试时,在Isr中停留过多时间会死机,软件的滞留不代表硬件中断的停留,这是硬件还在运行,还有可能在产生新的中断。