我的前面一篇文章已经总结了安装VS2005+Driverstudio+DDK来搭建环境(见http://blog.csdn.net/shejiannan/article/details/9128735)。下面再来一步一步来编写我们的第一个PCI驱动程序。(转载请指明出于shejiannan的csdn博客)
1.在安装好软件后,打开VS2005,TOOL目录下会多一个Driverstudio的选项,这里我们选择它新建一个工程。如下图
之后会出现一个工程引导界面,如下
然后一直点击NEXT直到第4步。如下图
这里我们选择PCI,下面的四个ID这里根据你的需要进行选择。至于这4个ID分别是什么意思可以在网上查找,也可以在我的这篇博客找到。链接http://blog.csdn.net/shejiannan/article/details/12614659。
点击下一步到第5步,点击界面中的ADD然后如下图选择要添加的硬件资源
然后点击NEXT到第7步,再点击ADD按钮,按下图方式选择
点击 OK后按下面方式选择,这里作下简单说明
驱动程序为了与用户程序进行正确的数据交换使用了如下二种方法:
一种是缓冲I/O(METHOD_BUFFRED)和直接I/O(METHOD_IN_DIRECT)。
另外还有一种是METHOD_NEITHER.这里数据量小,只是简单测试我们用直接IO就可以了。然后一起点击NEXT直接完成。Driverstudio会根据向导自动生成代码。其实我们刚才在引导界面时没有用中断,用的内存方式来访问设备。接下来我们分析一下代码
先看*****Driver.h文件
可以看出这里就三个函数
virtual NTSTATUS DriverEntry(PUNICODE_STRING RegistryPath); //驱动程序的入口
virtual NTSTATUS AddDevice(PDEVICE_OBJECT Pdo);//添加设备
virtual VOID Unload(VOID);//卸载设备
这里的三个函数是driverstuido自动生成的,而且帮我们实现了,我们不需要管它。
再来看***Device.h和***Device.cpp文件
可以看出这个类继承KPnpDevice类,而KPnpDevice提供驱动程序与应用程序的接口功能,承担IRP分发任务,提供与底层设备的接口功能,同时,提供与其他系统对象的接口功能。
生成的几个函数如下,每一个生成的函数系统都有注释说明
virtual NTSTATUS OnStartDevice(KIrp I);
virtual NTSTATUS OnStopDevice(KIrp I);
virtual NTSTATUS OnRemoveDevice(KIrp I);
virtual NTSTATUS OnDevicePowerUp(KIrp I);
virtual NTSTATUS OnDeviceSleep(KIrp I);
virtual NTSTATUS DefaultPnp(KIrp I);
virtual NTSTATUS DefaultPower(KIrp I);
这些我们都不需要管它,我们主要看Serial_IOCTL_800_Handler这个函数。这个函数是在StartIo()函数中被调用的,当驱动程加载时,并创建访问驱动程序的对象就会被调用。我们把这个函数主要修改为以下
void HNPCIdriverDevice::Serial_IOCTL_800_Handler(KIrp I)
{
NTSTATUS status = STATUS_SUCCESS;
// TODO: Verify that the input parameters are correct
// If not, return STATUS_INVALID_PARAMETER
// TODO: Handle the the IOCTL_800_ReadBase0 request, or setup for further processing.
// TODO: Assuming that the request was handled in this routine, set
// I.Information to indicate how much data to copy back to the user,
// and set I.Status to indicate the outcome of the IRP. Then
// complete this IRP and start the next IRP on the queue.
KMemory Mem(I.Mdl());
PULONG pInBuffer = (PULONG) I.IoctlBuffer(); //输入缓冲区,传来应用程序的一些参数
//以便通过应用程序指定读取的位置和数据个数
ULONG Offset; //从应用程序中获取传来的读取的偏移地址
Offset = *pInBuffer;
ULONG count; //从应用程序中获取传来的读取的数据个数
count = *(pInBuffer+1);
// Use the memory object to create a pointer to the caller's buffer
PULONG pOutBuffer = (PULONG) Mem.MapToSystemSpace(); //输出缓冲区
Resource0.ind(Offset,pOutBuffer,count);
I.Information() = count*sizeof(ULONG);
I.Status() = status;
IOCTL_800Queue.PnpNextIrp(I);
}
这样一个简单的驱动程序就完成了。在编译之前切记设置了DDK build settings中设置了DDK的安装路径,如下
可是当我们编译时可能还会出现问题。
1。当提示出现一个关于ntstrsafe.lib的错误时(具体忘记了),解决办法如下
(1)在工程的属性中把这个.lib去掉,如下图
(2)在source文件中把图下这行去掉
2.提示关于error C4430的错误,解决办法见我的文章http://blog.csdn.net/shejiannan/article/details/9792927
3.提示关于error LNK2001的错误,解决办法见我的文章http://blog.csdn.net/shejiannan/article/details/9793203
当我们选择FREE版本最后编译成功后会生成一个.INF和.SYS的文件。这两个文件我们需要它,在你的工程目录下,相信你能找到。
现在开始写应用程序开始测试,其实在生成驱动程序向导时Driverstudio就已经帮我们生成了一个应用程序,同样现在稍微修改就可以了。
我们在***Iorw.cpp文件中找到***ExexcuteIo函数中找到if ((!_tcscmp(str, _T("IOCTL_800"))) && ((ioItem->OutSize > 0) || (ioItem->InSize > 0)))处理相关的地方并在if里面添加如下,下面这段代码的意思是往偏移地址为0X00里读取一个32位长度的数据
ULONG bufInput[2]; // 传入读取的参数,其中第一个元素为指定的读取偏移地址,
// 第二个元素为指定的读取的数据个数
ULONG offset =0x00; // 定义的变量,用于存放要读取的偏移地址
//把获取的读取偏移地址赋值给bufInput第一个元素
bufInput[0]=offset;
//把要读取的数据个数赋值给bufInput第二个元素
bufInput[1]=1;
ULONG bufOutput[4];// 缓冲区,用于传出读取的数据,IOCTL_OUTBUF_SIZE在前面定义,为4
ULONG nOutput;
if ((!DeviceIoControl(
g_hDevice,
IOCTL_800,
bufInput,
2*sizeof(ULONG), // 字节数,为数组bufInput的大小
bufOutput,
1*sizeof(ULONG), // 字节数
&nOutput,
&ioItem->IoOverlapped
)) &&
(GetLastError() != ERROR_IO_PENDING))
{
error = GetLastError();
HNPCIdriverOutputText(_T("IOCTL_800 failed with error (%d)"), error);
break;
}
else
{
HNPCIdriverOutputText(_T("IOCTL_800 bufOutput (%x,%x,%x,%x,),nOutput(%d),offset(%d)"),
bufOutput[0],bufOutput[1],bufOutput[2],bufOutput[3],nOutput,offset);
// set data
TCHAR strdata[500]; ZeroMemory(strdata, sizeof(strdata));
for (ii = 0; ii < bufInput[1]; ii ++)
{
_stprintf(str, _T("%x"), bufOutput[ii]);
HNPCIdriverOutputText(_T("IOCTL_800 str(%s)"), str);
int len = _tcslen(str);
HNPCIdriverOutputText(_T("IOCTL_800 len(%d)"), len);
_tcsncat(strdata,str,8);
}
SetDlgItemText(hDlg, IDC_EDIT_DATA, strdata); //这里在界面上添加一个EDIT编辑框就可以了
}
好了,现在驱动程序和应用程序都OK,现在开始测试。
首先找到驱动程序生成的.INF.和SYS文件,安装驱动文件后在设备管理器中查看是否安装成功。如下图
然后再运行应用程序,如图
我们可以用WINDRIVER来验证下读到的数据是否正确。运行WINDRIVER后,效果如下
可以看到两个数据一模一样,说明我们的编写的程序OK。至此学习驱动程序又向前迈进一步。
结束语:文章讲得比较粗糙,对于PCI的有些概念没有说明,所以初学者应该先了解一下PCI的基本知识,另外我是在自己的电脑是进行测试,选择的PCI网卡进行测试,向导过程中的几个ID是通过WINDRIVER获得的(其实用WINDRIVER生成驱动更简单,有兴趣的可以研究学习下),结果安装驱动后搞得PCI网卡不能识别正常的网卡驱动程序导致我不能上网,不过我把自己写的驱动程序卸载后重启电脑系统又PCI网卡又重新安装并识别之前的驱动程序。所以大家做实验时最好用自己的PCI卡。