博主之前做过linux驱动开发,windows驱动还是新鲜事物,最近项目需要,研究了一段时间windows驱动,记录经验和感想如下:
先看总结
1、windows驱动网络上的资料较少,很多博客和书籍是基于古老的NT模型,学习途径主要是微软官方。
2、windows驱动没有设备树,但多了inf文件,以pcie设备驱动为例,linux中设备ID在设备树中配置,windows在inf文件配置。
3、虽然windows和linux驱动的api和开发方法有所差异,但整体模式和套路其实大差不差。都是创建一个设备文件或符号。对这个文件或符号open、read、write、close。linux下的ioctl对应windows下的EvtIoDeviceControl
4、windows驱动开发调试比linux要方便很多,使用vs就可以编译出目标驱动,基于网络就能单步调试。相比之前,linux驱动开发需要繁杂的配置工具链、编译环境;单步调试需要借助调试器。
搭建环境
目标电脑
在淘宝买二手的,现在大多数显示器都是hdmi的,最好注意主板是否有HDMI接口,否则要再买个VGA转HDMI模块
| 主板 | B85 | / |
|---|---|---|
| CPU | i3 4150 | / |
| 内存 | 8G | / |
| 硬盘 | 120G固态 | 主板、CPU、内存、硬盘、风扇套装 235¥ |
| 电源 | 长城300W | 35¥ |
| 合计 | 270¥ |
安装最新的win11系统
开发电脑
参照https://learn.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk安装开发工具
除了安装这些微软官方工具外,还可以安装一些辅助工具
| 工具 | 用途 |
|---|---|
| DebugView | 查看打印信息 |
| windbg | 进行单步调试 |
| winobj | 查看符号链接 |
| pciutils | 查看pcie设备信息 |
开发套路
目标电脑配置
进入测试模式,重启
bcdedit /set testsigning on
bcdedit.exe /set nointegritychecks on
shutdown -r -t 5
驱动匹配
以pcie设备为例,驱动通过inf文件中的ID进行匹配,其中10EE和8011为厂家ID和设备ID
[Standard.NT$ARCH$.10.0...16299]
%test.DeviceDesc% = test_Device, PCI\VEN_10EE&DEV_8011&SUBSYS_000710EE ;
除此之外,inf文件还描述了设备的类型、版本等,类似于linux下的设备树
代码入口
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
...
}
创建设备符号
// 创建给应用的符号接口
status = WdfDeviceCreateDeviceInterface(
Device_G,
&GUID_DEVINTERFACE_test,
NULL // ReferenceString
);
这个符号定义在Public.h文件中,示例如下:
DEFINE_GUID (GUID_DEVINTERFACE_test,
0xf0041375,0xe829,0x4ea2,0xb9,0x64,0x6a,0x24,0x0b,0xe0,0xfc,0xf3);
ioctl
回调函数中实现ioctl
queueConfig.EvtIoDeviceControl = testEvtIoDeviceControl;
VOID testEvtIoDeviceControl(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode)
通过WdfRequestRetrieveInputBuffer和WdfRequestRetrieveOutputBuffer函数实现内核和应用层数据交互
struct FDEV_IOCTRL_WRITE_STR *p_u2k_buf, *p_k2u_buf;
status = WdfRequestRetrieveInputBuffer(Request, sizeof(struct FDEV_IOCTRL_WRITE_STR), &p_u2k_buf, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
return;
}
status = WdfRequestRetrieveOutputBuffer(Request, sizeof(struct FDEV_IOCTRL_WRITE_STR), &p_k2u_buf, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
return;
}
通过WdfRequestCompleteWithInformation函数配置内核返回给应用的数据长度
// !!!设置实际返回的字节数(必须调用此函数,否则应用程序收不到数据)
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(struct FDEV_IOCTRL_WRITE_STR));
完结
以上就是windows驱动开发的简要经验,整体其实和linux的套路是类似的,只是接口函数存在差异。
为了在较短时间实现pcie设备的驱动,可以借鉴linux下UIO驱动的思想,在内核中实现内存读写、中断处理、内存分配和DMA操作等关键接口。具体的设备驱动在应用层通过c++来实现。
1016

被折叠的 条评论
为什么被折叠?



