您的位置:首页 > 其它

用VS2005+Driverstudio+DDK开发第一个PCI驱动程序(详解)

2017-01-17 13:45 591 查看
我的前面一篇文章已经总结了安装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卡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: