Linux驱动与应用层通信,USB设备驱动层与应用层和硬件的通信过程.

1、USB设备驱动程序

(1)驱动程序的基本概念设备驱动程序是一个包含了许多操作系统可调用例程的软件容器,它可以使得应用程序访问硬件设备,这些例程可以使硬件设备执行相应的动作。也就是处理硬件设备连接到CPU通信的细节的代码。硬件设备可能是任何连接到计算机的电子电路。一个设备驱动应使得应用程序远离细节,使应用程序不必知道物理连接、信号和与一个设备通信需要的协议等。应用程序是用户运行的程序,包括支持自定义硬件的特殊用途的应用程序。设备驱动可以保证程序代码只通过外设句柄访问外设或者端口。应用程序不需要精确监视和控制外设需要的交换信号(忙、应答等)。设备驱动通过在应用层和硬件专用层专用代码之间的转化来完成它的任务。应用层代码一般使用一套操作系统支持的函数。硬件代码则处理那些访问外设电路的必要协议,包括检测状态信号和在合适的时间切换控制信号。

(2)WDM设备驱动简介在Windows98下运行的代码以以下两种模式之一运行:用户模式和内核模式。在访问内存和其它系统资源时,每种有不同的允许优先级。应用程序必须运行在用户模式下,其它很多驱动运行在内核模式下。

在用户模式下,Windows限制应用程序访问内存和其他系统资源,Windows不允许用户访问设定为被保护的内存区域。这使得PC机可同时运行多个应用程序,不会互相干扰从理论上讲,即使一个应用程序崩溃了,其它应用程序也不会受到影响。在内核模式下,代码不限制访问系统资源,包括执行内存管理指令和控制访问I/O端口。

每个应用程序和驱动使用自己的语言与操作系统通信。应用程序使用WIN32API函数。驱动通信使用称作U0请求包(IRP)的结构。Windows定义了一套驱动可以使用的IRP。每个IRP请求或执行一个单个的输入或输出动作。USB设备的设备驱动使用IRP传递总线通信,处理USB通信。总线驱动按顺序使用IRP传递临近总线的通信。在一系列通信中,最终的总线直接驱动硬件。总线驱动被包括在Windows系统里,且不需要我们编程。

当用户模式程序需要读取设备数据时,它就调用 Win32 API 函数, Win32 子系统模块 ( 如 KERNEL32.DLL) 通过调用平台相关的系统服务接口实现该 API, 而平台相关的系统服务将调用内核模式支持例程。调用首先到达系统 DLL 中的一个入口点,然后这个用户模式的函数接着调用系统服务接口,最后由系统服务接口调用内核模式中的服务例程 [39] 。服务例程运行在内核模式中,为应用程序请求提供服务,并以某种方式与设备交互。它们首先检查传递给它们的参数以保护系统安全或防止用户模式程序非法存取数据,然后创建一个 IRP 的数据结构,并把这个数据结构送到某个驱动程序的入口点。用户模式调用者得到一个返回值,表明该 IRP 代表的操作还没有完成。用户模式程序也许会继续其它工作然后等待操作完成,或者立即进入等待状态。不论哪种方式,设备驱动程序对该 IRP 的处理都与应用程序无关。执行 IRP 的设备驱动程序最后可能会访问硬件。驱动程序完成一个 Il0 操作后,通过调用一个特殊的内核模式服务例程来完成该 IRP 。完成操作是处理 IRP 的最后动作,它使等待的应用程序恢复运行。

(3) USB 设备驱动的总线驱动 与传统 PC 总线 ( 如 PCI 总线 ) 设备的驱动程序相比, USB 设备驱动程序从不直接与硬件对话。相反,它仅靠创建 URB(USB request blocks) ,并把 URB 提交到总线驱动程序就可完成硬件操作。

系统中的 USB 总线驱动程序完成许多的工作。实际上对于一些 HID( 人机接口 ) 的 USB 设备,象键盘,鼠标和游戏操纵杆之类的设备可以自动的被系统识别并且支持。而除此之外的设备就需要自己写一个驱动程序来完成硬件和软件之间的联系。在核心模式 (kernel mode) 下,驱动程序用内部 I/O 控制 (IOCTL) 来组织和操作一些由其他部分发过来的要求和命令。而 IOCTL 又是通过 URB 来实现数据的传送的。

可以把 USBD.SYS 看作是接受 URB 的实体,向 USBD 的调用被转化为带有主功能代码为 IRPwe MJee INTERNAL DEVICE_ CONTROL 的 IRP 。然后 USED 再调度总线时间,发出 URB 中指定的操作。

为了创建一个 URB ,首先应该为 URB 分配内存,然后调用初始化例程把 URB 结构中的各个域填入请求要求的内容,例如,当为响应 IRP START DEVICE请求而配置设备时,首要的任务就是读取该设备的设备描述符。还可以在系统堆上为 URB 动态地分配内存。创建完 URB 后,需要创建并发送一个 IOCTL 请求到 USBD 驱动程序, USBD 驱动程序位于驱动程序层次结构的低端。在大多数情况下,需要等待设备回应。 当提交一个 URB 到 USB 总线驱动程序时,最终将收到一个描述该操作结果的 NTSTATUS 代码。其间,总线驱动程序使用另一组类型名为 USBD_ STATUS 的状态代码。这些代码并不是 NTSTATUS 代码。当 USED 完成一个 URB 时,它就把 URB 的 UrbHeader.Status 域设置为某个 USBD_ STATUS 值。并没有特别的协议关于保留这个值以及把它传递回应用程序,可以随意处理这个值。

在使用总线驱动中有两个问题需要重点说明:

读取配置描述符 因为硬件不允许直接访问接口和端点描述符,所以必须把整个可变长结构读入一个连续的内存区。读配置描述符时使用的描述符索引来自于设备固件响应 GET DESCRIPTOR 控制请求的结果,而不受 USB 规范的限定 [4U) 。配置过程的下一步是创建 URB 并发送到设备。 除了创建 URB, USBD 还初始化 USBD_INTERFACE_ LIST 表项中的 Interface 成员,使其指向一个 USBD INTERFACE INFORMATION 结构。这个结构与 URB 在同一物理内存区,在释放 URB 时该结构一同被释放。其中管道信息结构是我们在此真正关心的,因为结构中的其它域是在提交 URB 后由 USBD 填充的。这样,我们己经有了一组 USBD_INTERFACE LIST 表项。

配置 USB 总线驱动程序自动检测新插入的 USB 设备。然后它读取设备内的设备描述符以查明插入的是何种设备,描述符中的厂商和产品标识以及其它描述符一同决定具体安装哪一个驱动程序。

配置管理器调用驱动程序的 AddDevice 函数。 AddDevice 做所有我们已知的任务:创建设备对象,把设备对象连接到驱动程序堆栈上等等。最后,配置管理器向驱动程序发送一个即插即用请求 IRP_MN_START_DEVICE 。现在在写驱动程序中 . 不必再为 USB 驱动程序的 I/O 资源担心,因为它们不用任何 I/O 资源。这里可以直接说配置设备而不是配置硬件,因为不再涉及 I/O 端口、中断、 DMA 适配器对象,或者任何其它面向资源的元素

USB 使用了许多方法来帮助操作系统定位驱动程序 ( 或驱动程序集 ) ,包括设备上的设备描述符、配置描述符,以及接口描述符。对于有厂商和产品标识的设备,配置管理器首先在注册表中查找具有相应 ID 号的设备。如果注册表中没有这个表项,配置管理器将触发“新硬件向导”来寻找该设备的 INF 文件。新硬件向导向用户询问 INF 文件的位置,然后安装驱动程序并填写注册表。一旦配置管理器找到了注册表表项,它就可以动态地装载驱动程序。

2.USB应用程序设计

在Win32系统中,把每一个设备都抽象为文件,此时的应用程序只需通过几条简单的文件操作API函数,就可以实现与驱动程序中某个设备通信。一个驱动程序可以驱动多个设备,并且此驱动可能为Windows已有的,也可能为用户安装的。 通常,这些Win32API函数有以下几种:

(1) 查找打开设备 CreatFile 函数。打开一个设备,返回一个与设备相关的句柄。该函数声明形式如下:

HANDLE CreatFile( LPCTSTR lpFileName , //所要打开的设备名

DWORD dwDesiredAccess , //访问模式

DWORD dwShareMode , //共享模式

LPSECURITY_ATTRIBUTES lpSecurityAttributes ,//通常为NULL

DWORD dwCreationDistribution , //创建模式

DWORD dwFlagsAndAttributes , //文件属性和标志

HANDLE hTemplateFile // 临时文件的句柄,通常为NULL ) ;

如果调用成功,那么函数返回打开设备的句柄。采用下面程序来实现:

HANDLE hCom HCom=CreatFile( “COM2” , //设备名

GENERIC_READ | GENERIC_WRITE , //容许读和写

0 , //独占方式 NULL ,OPEN_EXISTING , //打开而不是创建

FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, //

重叠方式

NULL) ;

当COM2口存在且处于空闲时,CreateFile函数将返回该串口的句柄。然而在编写USB应用程序时,却没有这么简单。因为目前还不知道所要打开的USB端口的设备路径名,所以还要获取设备信息集,识别接口信息。

(2) 从设备中读、写数据 ReadFile 函数。从设备中读取数据。

该函数声明如下:

BOOL ReadFile( HANDLE hCOM , //设备句柄

LPVOID lpBuffer , //指向接收缓冲区的指针

DWORD nNumberOfBytesToRead , //指定所要的字节数

LPDWORD lpNumOfBytesRead , //指向调用该函数读出的字节数

LPOVERLAPPED lpOverlapped , //异步结构 ) ;

其中,参数hCom为CreateFile函数所打开的串口句柄。

WriteFile 函数。向设备写函数,该函数声明形式如下:

BOOL WriteFile ( HANDLE hCom , //设备句柄

LPVOID lpBuffer , //指向读数据缓冲区的指针

DWORD nNumberOfBytesToWrite , //所读的字节数

LPDWORD lpNumOfBytesWritten , //指向已读入的字节数

LPOVERLAPPED lpOverlapped , //异步结构 ) ;

其中,参数hCom为CreateFile函数所打开的串口句柄。

(3) 设备的控制操作 DeviceIoControl 函数。

对设备进行一些自定义的操作,比如更改设置等。该函数声明形式如下:

BOOL DeviceIoControl ( HANDLE hDevice , //设备句柄

WORD dwIoControlCode , //所要执行的操作命令码

LPVOID lpINBuffer , //输入缓冲区

DWORD nInBufferSize , //输入缓冲区空间大小

LPVOID lpOutBuffer , //接收缓冲区

DWORD nOutBufferSize , //接收缓冲区

LPDWORD lpBytesReturned , //实际所接收数据个数

LPOVERLAPPED lpOverlapped , //异步结构 ) ;

其中,参数hDevice为CreateFile函数所打开的串口句柄。

(3) 关闭设备 CloseFile 函数。关闭一个由CreatFile打开的设备。该函数声明形式如下:

BOOL CloseHandle(HANDLE hCom)

其中,参数hCom为CreateFile函数所打开的串口句柄。 这些API函数的执行,都对应着驱动程序的一些分发例程,表5.1是常用API函数和驱动程序的IRP对应关系表。例如,当应用程序调用函数CreateFile来打开设备对象时,操作系统代替应用程序,向驱动程序发送系统I/O控制消息IPR_MJ_CREATE,从而驱动程序响应这个消息,对应的例程被调用。如果驱动程序没有提供该例程,CreateFile调用就会失效。也许有这个疑问:当用CreateFile打开一个串口时,并没有编写驱动程序,为什么串口已有驱动程序?这是因为串口的驱动程序是不需自行编写的,它已包含在系统中。这样,编写好相应的USB驱动程序后,再编写应用程序就不是一件很困难的事情。

表5.1 常用API函数和驱动程序的IRP对应关系

API 函数 IRP 说 明

CreateFile IRP _MJ_CREATE 打开设备

ReadFile IRP_MJ_READ 从设备获取数据

WriteFile IRP_MJ_WRITE 向设备发送数据

CloseFile IRP_MJ_CLOSE 关闭设备

DeviceIoControl IRP_MJ_DEVICE_CONTROL 控制操作

应用层API(kernel32.dll)

驱动层的IRP

驱动层创建URB(USBD.SYS),同时创建并发送一个IOCTL请求到USBD 驱动程序

驱动将URB提交到总线驱动程序

总线驱动程序配置硬件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值