广西工学院鹿山学院本科生毕业设计(说明书)
包括以下几种事件:
#define SERIAL_EV_RXCHAR 0x0001 #define SERIAL_EV_RXFLAG 0x0002 #define SERIAL_EV_TXEMPTY 0x0004 #define SERIAL_EV_CTS 0x0008 #define SERIAL_EV_DSR 0x0010 #define SERIAL_EV_RLSD 0x0020 #define SERIAL_EV_BREAK 0x0040 #define SERIAL_EV_ERR 0x0080 #define SERIAL_EV_RING 0x0100 #define SERIAL_EV_PERR 0x0200 #define SERIAL_EV_RX80FULL 0x0400 #define SERIAL_EV_EVENT1 0x0800 #define SERIAL_EV_EVENT2 0x1000 (7)、IOCTL_SERIAL_WAIT_ON_MASK
这个IO控制码是最重要的一个,当应用程序通过前面几个IO控制码初始化好后,就会发送这个请求。在驱动程序中,应该阻塞在那里返回PENDING状态,而不是完成这个IRP。当IOCTL_SERIAL_WAIT_ON_MASK设置的事件中的一项发生时,阻塞状态改为完成,并通知应用程序究竟是哪种事件发生了。
串口驱动程序完成处理IO控制码之后,接下来就是对读写IRP进行处理了,对于写IRP的派遣函数中,主要写的数据存储在设备扩展中,以便以后读的时候将这些内容返回应用程序,另个使阻塞的IO控制苏醒过来,调用DriverCheckEvents函数阻塞IRP完成,使应用程序的线程得以继续运行。并且这个线程还知道了SERIAL_EV_RXCHAR和
SERIAL_EV_RX80FULL事件的到来,从而发起一个读请求,传送到驱动程序中就是读IRP。在读IRP派遣函数中,负责将存储在设备扩展中的数据通过IRP传送到应用程序。
5.4 使用DDK设计USB转串口驱动程序
5.4.1 USB转串口的驱动程序基本框架
与一般的应用程序流程类似,USB转串口的驱动程序流程也可分为初始化、数据处
21
广西工学院鹿山学院本科生毕业设计(说明书)
理、判断是结束设备等部分。驱动程序流程图如下图所示:
图5.3驱动程序流程图
系统初始化:此部分主要DriverEntry函数来处理。DriverEntry是驱动程序的入口点,该函数由I/O系统直接调用。
在函数中获取设备的注册表信息、并对以下的IRP指定相应的派遣函数: DriverObject->MajorFunction[IRP_MJ_CREATE] = USB2COM_Create; DriverObject->MajorFunction[IRP_MJ_CLOSE] = USB2COM_Close; DriverObject->DriverUnload = USB2COM_Unload; // 用户模式DeviceIoControl()调用此例程
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=USB2COM_ProcessIOCTL; // 用户模式ReadFile()/WriteFile()调用此例程
DriverObject->MajorFunction[IRP_MJ_WRITE] = USB2COM_Write; DriverObject->MajorFunction[IRP_MJ_READ] = USB2COM_Read; //系统PNP 与电源管理请求处理例程
22
广西工学院鹿山学院本科生毕业设计(说明书)
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]= USB2COM_ProcessSysControlIrp;
DriverObject->MajorFunction[IRP_MJ_PNP]= USB2COM_ProcessPnPIrp; DriverObject->MajorFunction[IRP_MJ_POWER]= USB2COM_ProcessPowerIrp; 指定IRP派遣函数后,接下来AddDevice例程创建设备对象 // 当设备插入时会调用此例程
DriverObject->DriverExtension->AddDevice= USB2COM_PnPAddDevice;
对USB设备而言,首先要检查设备是否已启用,如有必要复位设备所在的端口。使用IOCTL_INTERNAL_USB_GET_PORT_STATUS和IOCTL_INTERNAL_USB_RESET_PORT来构造IRP,并将NextIrpStack->Parameters.Others.Argument1置为NULL,进而调用IoCallDriver发送。
接下来要用合适的接口描述符配置设备和初始化管道:使用函数UsbBuildGetDescriptorRequest构造获取配置描述符的URB,并发送,这一步完成后,将获得设备配置描述符;用函数USBD_PraseConfigurationDescriptorEx在获得的配置描述符中查找满足条件的接口描述符,并用它们填充USBD_INTERFACE_LIST_ENTRY类型的数组,即接口描述符列表;再将接口描述符列表作为参数,调用函数USBD_CreateConfigurationRequestEx构造配置设备接口的URB,并发送,完成设备接口的配置。至此,初始化工作完成。
5.4.1 makefile文件与source文件
Makefile文件指定文件之间的相互依赖关系,确定项目中哪些文件需要重新编译。在DDK程序进行编译时,build工具会调用nmake工具去解析makefile文件进行编译。大多数情况下,makefile文件只需写入如下内容即可:
!INCLUDE $(NTMAKEENV)\\makefile.def
以上语句的含义是包含了DDK目录中的makefile.def
用DDK编译环境编译驱动程序还需要编写一个编译脚本文件source,在这个脚本中描述了DDK驱动程序的源文件、用到的lib文件和include路径名、编译输出的目录和文件名等信息,本项目的source文件内容如下:
TARGETNAME=usb2rs232
23
广西工学院鹿山学院本科生毕业设计(说明书)
TARGETTYPE=DRIVER DRIVERTYPE=WDM
TARGETPATH=obj
TARGETLIBS=$(DDK_LIB_PATH)\%usbd.lib
USE_MAPSYM=1 USER_C_FLAGS=/FAcs SOURCES= \\ usb2com.rc \\ dbg.c \\ usb.c \\ pnp.c \\ power.c \\ ioctl.c \\ rw.c \\ serial.c \\ buf.c \\ waitmask.c \\ purge.c
TARGETNAME:描述目标驱动程序的名称。
TARGETTYPE:描述目标代码生成的类别。TARGETTYPE=DRIVER意味着是生成驱动,如果TARGETTYPE=PROGRAM,则编译成Win32程序。
TARGETPATH:指示目标代码生成的路径。 TARGETLIBS:设置目标代码所需要的库。
SOURCES:指定此工程所有的源文件,只指定C文件或者C++文件,不需指定H文件。
5.4.3 修改安装驱动用的inf文件
24
广西工学院鹿山学院本科生毕业设计(说明书)
安装驱动时,要指定INF文件,该文件中应包含了安装驱动时匹配的硬件ID号、需要增加的设备类、设备名等信息。一般来说,INF文件可以从网上下载,然后修改相应部分即可:
在[version]节,Class类为端号,GUID为端口类的GUID。 Class=Ports
ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
在[Pro]节,需要指定硬件匹配的ID号,要本题中为PID与VID均为9999 TviceDesc% = ComPort, USB\\VID_9999&PID_9999
在[DestinationDirs]节为目标文件夹的位置,12为system32目录 DefaultDestDir=12
在[Strings]节,指定一些供显示用的字符串,可按自己的需要来修改,不影响驱动的安装。
Pro = \
Pro.Disk=\DeviceDesc = \
Serial.SVCDESC = \
安装好设备驱动后,在电脑上就可以访问此虚拟的串口设备了,如用串口调试助手,如同操作普通串口一样,打开COM4然后进行数据的传送。
25