对USB驱动程序的理解(1)
学了两个多星期了,对USB驱动程序的有一小小的理解。现在总结如下!!!!!!!!
1、每个设备对应一个PDO,每个PDO又对应多个FDO,在驱动程序中直接操作的不是硬件而是相应的PDO和FDO。在USER和KERNEL通信方面,系统将每一个用户请求打包成IRP结构,将其发送至驱动程序,并通过识别IRP中的PDO来区别是发送给哪个设备的。另外,在驱动程序加载方面,WDM不通过驱动程序名称识别,而是通过一个128位的全局唯一的标识符GUID来实现驱动程序的识别,即每一个固件都有一个GUID,你通过GUID来区分是哪个固件。
2、USB主机驱动符合WDM驱动体系结构也使用IRP(I/O请求包)的机制。但实际的USB驱动程序使用URB(USB请求块)结构向其硬件设备发送请求。USB驱动程序高度依赖其总线驱动程序(USBD.SYS),而不直接使用硬件抽象层(HAL)函数与硬件通信。
3、USBD是USB系统软件中最关键的一层,它负责控制全部USB协议的操作和中断处理控制。主要功能有:设备设置、资源管理、数据传输(管道层次)以及公共数据定义等。管道机制和命令机制。管道机制:管道是设备和主机之间的逻辑连接,分为标准管道和逻辑管道,标准管道用语完成一些客户通过命令接口所传递的请求,如设置设备的地址等,但USBD不允许客户直接访问设备的标准管道。客户管道则有客户进行管理,并提供相应的数据缓冲区。命令机制允许客户以读写的方式对设备的数据及其控制部分进行访问,客户所要做的,仅仅是向USBD提供设备的地址及相关的数据缓冲区的指针。命令机制所提供的功能主要是USB总线管理相关的内容,如设备设置管理、设备数据访问、总线设备管理以及电流分配等。
4、USB设备驱动程序设计
2000DDK+VC6.0(只能用C,不能用C++)
三个关键的例程:DriverEntry、AddDevice、ReadWrite
(1)DriverEntry,完成某些全局初始化操作,它是内核模式驱动程序主入口点。
extern "C"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )
通过PDRIVER_OBJECT这一重要的数据结构,I/O管理器使用它来定义每一个设备驱动程序,DriverEntry主要工作是各种函数指针填入驱动程序对象的各个域中,这些指针为操作系统指明了驱动程序容器中各种子例程,重要包括下面指针成员:
1、DriverUnload
2、DriverExtension->AddDevice指向驱动程序的AddDevice函数
3、DriverStartIo指向驱动程序处理串行I/O请求的函数。如果驱动程序采用标准的IRP排队的方式,应该设置该成员,使得指向驱动程序的StartIo例程。
4、MajorFunction是一个指针数组,指向存在于驱动程序中的多种IRP处理函数。
DriverObject->DriverUnload = DriverUnload;
DriverObject->DriverExtension->AddDevice =AddDevice;
DriverObject->MajorFunction[IRP_MJ_CREATE]= DispatchCreate;
~~~~~~~~~~~~~~
(2)AddDevice
一个驱动程序可以被多个设备使用。WDM驱动程序有一个特殊的AddDevice函数,PnP管理器为每个实例调用该函数。原形如下:
NTSTATUS
AddDevice( IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject);
AddDevice函数的基本职责是创建一个设备对象并把它连接到以PDO为底的设备栈中。
具体步骤如下:
1) 用IoCreateDevice创建设备对象,并建立一个私有的设备扩展对象。
PDEVICE_OBJECT fdo;
NTSTATUS status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&fdo);
2) 初始化设备扩展和设备对象的Flag成员。
PDEVICE_EXTENSION pdc = (PDEVICE_EXTENSION)fdo->DeviceExtension;
fdo->DeviceExtension;
fdo->Flag2|=DO_BUFFERED_IO;
3) 调用IoAttachDeviceToDeviceStace函数把新设备对象放到堆栈上。
pdx->LowerDeviceObject=IoAttachDeviceToDeviceStack(fdo,pfo);
设备对象(DEVICE_OBJECT或*PDEVICE_OBJECT)也是DDK定义的一个重要结构。它可以在其设备名称(DeviceName)域中(第三个参数)为设备对象进行命名。 设备对象另一个关键的域为扩展设备对象(DeviceExtension)大小,I/O管理器为设备对象分配一块内存,该指针指向一个用户定义的数据结构,用于保存每个设备实例的信息
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject; //device object this extension
//belongs to
PDEVICE_OBJECT LowerDeviceObject; //next lower driver in//same stack
PDEVICE_OBJECT Pdo; //the PDO
IO_REMOVE_LOCK RemoveLock; //removal control locking structure
UNICODE_STRING ifname; //interface name
DEVSTATE state //current state of device
DEVSTATE prevstate; //state prior to removal query
DEVICE_POWER_STATE devpower; //current device power state
SYSTEM_POWER_STATE syspower; //currnet system power state
DEVICE_CAPABILITIES devcaps; //copy of most recent device capabilities
LONG handles; //#open handle
USBD_DEVICE_DESCRIPTOR dd; //device descriptor
USBD_CONFIGURATION_HANDLE hconfig; //selected configuration handle
PUSB_CONFIGURATION_DESCRIPTOR pcd; //Configuration descriptor;
LANGID langid; //default language id for strings
USBD_PIPE_HANDLE hPipe;
USBD_PIPE_HANDLE hPipe_WriteCode;
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;
AddDevice为设备对象注册一个接口,以便应用程序能通过注册接口来访问该设备。一个设备接口被一个128位的GUID唯一标识。
status = IoRegisterDeviceInferface(pdo,&GUID_INTERFACE_USB,NULL,&pdx->ifname);得到GUID后,当响应PnP请求IRP_MN_START_DEVICE时,驱动程序调用下面函数使其可用,IoSetDeviceInterfacestate(&pdx->ifname,TRUE);
在响应这个调用过程中,I/O管理器将创建一个指向设备的PDO符号连接对象。因为接口名最终指向PDO,所以PDO的安全描述符将最终控制设备的访问权限。(3)ReadWrite实现设备驱动程序功能的各个派遣函数,并实现总线枚举和管理函数,用于设备初始化以及错误恢复。是整个驱动最重要部分,以读数据请求为例子,即主机要求设备向其传输数据。
1)建立并提交一个URB,USB驱动从不直接和硬件对话,通过创建URB并把URB提交到总线驱动程序就可以完成硬件操作,USBD.SYS是接受URB的实体,向USBD的调用被转化为带有主功能代码为IRP_MJ_XXX的IRP。然后USBD再调度总线时间,发出URB中指定的操作。
创建一个URB是USB驱动最基本的工作,首先应该为URB分配内存,然后调用初始化例程把URB结构中的各个域填入请求要求的内容。最后通过创建并发送一个内部I/O控制(IOCTL)请求到USBD驱动程序来发送这个URB包,从而完成USB请求。
2)创建并发送IOCTL请求
创建完URB后,我们创建并发送一个IOCTL(内部I/O控制)请求到USBD,然后等待设备回应,相应的函数为SendAwaitUrb
3) 返回设备完成状态