关于USB无线网卡的实现

时间:2022-01-16 16:03:39

本文主要介绍了USB无线网卡的实现方案,涉及基于微软NDIS框架下的网卡驱动开发,802.11无线协议开发,USB协议开发部分。NDIS框架式微软专门针对网络设备设计的接口。由于是USB接口,所以需要一个WDM的底层作为NDIS miniport驱动的物理访问接口。作为无线网卡,对802.11协议的实现是本文很重要的一个内容。

关键词:NDIS WDM USB 802.11

1,  项目介绍

本项目是USB无线网卡项目。内容涉及到硬件开发,驱动框架开发,802.11协议开发几个部分。

首先简单介绍硬件结构,硬件的实现方案如下图1-1网卡工作在半双工状态。USB接口依USB 2.0,采用ICSIIC9211芯片。物理层芯片使用Intersil公司芯片。MAC层控制引擎需要使用微码编程或嵌入CPU Core

 

 

作为一个新的项目,首先要列出开发的工作量,制定出合理的开发计划,将整个项目细分为几个小的项目,一步一步推进。下面介绍一下工作内容。

这些内容中,首先要解决的是USB通信问题,由于硬件接口为USB总线接口,所以必须要打通USB通道。微软windows系统中有专门的编程接口,我们只要将需要的功能封装成URBUSB Request Block)请求包,通过调用USBD class driver接口,将URB包发到下一层。最总由总线驱动与设备交互。

其次是开发NDIS miniport驱动框架。NDIS是微软的网络设备的开发库,提供了许多和网络设备相关的接口函数供调用。根据设计,本驱动为一个miniport类型的驱动,所以需要开发标准的miniport接口供系统调用。和传统的驱动一样,也是封装一系列的回调函数给系统,这些调用接口形式微软的NDIS规范已经定义好了。

       接下来就是对这些接口的完善。加入802.11协议内容,作为驱动的实体。由于intersil的芯片没有集成802.11协议功能,所以我们需要在软件中开发协议中的MAC层规定的内容。这部分也是我们驱动的主要内容。

802.11协议有三个主要状态。根据协议规范只有在关联的状态下,才可以进行数据通信。认证,加密是安全相关的,理论上与协议没有必然关系,802.11无线协议为了安全,将加密和认证也作为协议的一部分,从而使得其具有等同有线网络的安全级别。同时,802.11协议定义很多命令,如Probe request/responseBeaconAuthenticationDeauthenticationAssociationDeassociation等,程序的主体是一个状态机,只要发送相应的命令就可以进行状态切换,完成相应的功能。

       协议中涉及到AP列表的搜索,认证过程,关联过程,信道的切换等需要分别实现,分别是一个更细节的状态机。关于802.11协议详细内容请参考相关的文档。这里不多说了。下图1-2802.11协议的状态图。

 

 

       按照功能划分:可分为管理、控制和数据3种不同类型帧。Class 1Class 2&3 Frame的定义请参考802.11-1999

 

 

1,  Usb总线介绍

USBIntel公司开发的通用串行总线架构,以简单的设计,易用性,热插拔特性受到了广泛的欢迎,很多设备都开始支持USB规范。

    一个USB系统主要被定义为三个部分:USB的互连;USB的设备;USB的主机。

在任何USB系统中,只有一个主机。USB和主机系统的接口称作主机控制器,主机控制器可由硬件、固件和软件综合实现。根集线器是由主机系统整合的,用以提供更多的连接点。

21 总线的拓扑结构


2-1显示了USB总线的拓扑结构。

 

 

标准USB规范有四根线,分别是电压正负极,两根数据线。外观为扁平的方形接口。USB传送信号和电源是通过一种四线的电缆,图2-2中的两根线是用于发送信号。电缆中包括VBUS、GND二条线,向设备提供电源 。VBUS使用+5V电源。

 

 

USB总线属一种轮讯方式的总线,主机控制端口初始化所有的数据传输。在USB设备安装后,主机通过设备控制通道激活该端口并以预设的地址值给USB设备。主机对每个设备指定唯一的USB地址。

USB定义了一些请求命令,所有的USB设备在设备的缺省控制通道(Default Control Pipe)处对主机的请求发出响应。这些请求是通过使用控制传输来达到的,请求及请求的参数通过Setup包发向设备,由主机负责设置Setup包内的每个域的值。每个Setup包有8个字节。见表2-1

 

 

 

bmRequestType

    这个域表明此请求的特性。特别地,这个域表明了第二阶段控制传输方向。如果wLength域被设作0的话,表明没有数据传送阶段,那Direction位就会被忽略。

    USB说明定义了一系列所有设备必须支持的标准请求。这些请求被例举在表8-3中。另外,一个设备类可定义更多的请求。设备厂商也可定义设备支持的请求。

    请求可被导引到设备,设备接口,或某一个设备端结点(endpoint)上。这个请求域也指定了接收者。当指定的是接口或端结点(endpoint)时,wIndex域指出那个接口或端节点。

bRequest

    这个域标识特别的请求。bmRequestType域的Type啦可修改此域的含义。本说明仅定义Type 字位为0即标准设备请求时bRequest域值的含义。

wValue

    此域用来传送当前请求的参数,随请求不同而变。

wIndex

wIndex域用来表明是哪一个接口或端结点,图2-3表明wIndex的格式(当标识端结点时)Direction位在设为0时表示出结点,设为1时表示是入结点,Endpoint Number是结点号。图2-4表明wIndex用于标识接口时的格式。

 

 

 

wLength

    这个域表明第二阶段的数据传输长度。传输方向由bmRequstType域的Direction位指出。wLength域为0则表明无数据传输。在输入请求下,设备返回的数据长度不应多于wLength,但可以少于。在输出请求下,wLength指出主机发出的确切数据量。如果主机发送多于wLength的数据,设备做出的响应是无定义的。

 

    2-2描述了所有USB设备都定义的标准设备请求将它们列出。不管设备是否被分配了非缺省地址或设备当前是被配置了的,它们都应当对标准请求产生响应。具体请参考USB规范。

 

 

 

讲到USB总线,就不得不讲USB总线的传输方式。

一个USB通道是设备上的一个端点和主机上软件之间的联系。USB设备的设计者可以决定设备上每个端点的能力。一旦为这个端点建立了一个通道,这个通道的绝大多数传送特征也就固定下来了,一直到这个通道被取消为止。

USB定义了4种传送类型:

·控制传送:可靠的、非周期性的、由主机软件发起的请求或者回应的传送,通常用于命令事务和状态事务。

·同步传送:在主机与设备之间的周期性的、连续的通信,一般用于传送与时间相关的信息。这种类型保留了将时间概念包含于数据中的能力。但这并不意味着,传送这样数据的时间总是很重要的,即传送并不一定很紧急。

·中断传送:小规模数据的、低速的、固定延迟的传送。

·批传送:非周期性的,大包的可靠的传送。典型地用于传送那些可以利用任何带宽的数据,而且这些数据当没有可用带宽时,可以容忍等待。

每一个端点Endpoint都有自己的传输方式,通过配置描述符,主机就可以知道每一个端点的传输方式,从而以该方式进行通信。

1,  WDM框架介绍

WDMwindows driver model)是微软对于驱动开发定义的一整套规范。与原来的Vxd驱动相比,WDM更加封装完好,并且可以对Windows各版本的做到二进制兼容,在一个系统下完成了,可以很轻松的移植到其他windows系统下。本人有幸将windows xp下开发的驱动,移植到了windows 2000,window mewindows 98下。这是我很好的经验。

根据Walter oney的观点,设备驱动程序是一个包含了许多操作系统可调用例程的容器,这些例程可以使硬件设备执行相应的动作。

WDM模型使用了层次结构,如图3-1所示,左边是一个设备对象堆栈。设备对象是系统为帮助软件管理硬件而创建的数据结构。一个物理硬件可以有多个这样的数据结构。出于堆栈最底层的设备对象称为物理设备对象(physical device object),简称PDO。在设备对象堆栈的中间摸出有一个对象称为功能设备对象(function device object),简称FDO。在FDO的上面和下面还会有过滤器设备对象(filter device object),简称FiDO

 

 

总线是个广义的定义,包括PCISCSI卡,并行口,串行口,USB集线器(hub),等等。实际上它可以是任何能插入多个设备的硬件设备。总线驱动程序的一个任务就是枚举总线上的设备,并为每一个设备创建一个PDO

我们的miniport其实也是一个功能驱动程序。

 

1,  开发环境介绍

下面介绍一下开发环境,windows驱动开发使用VC工具,也可以使用脚本直接编译,关键是设置好环境变量,DDK库的路径设置好。编译出的驱动有checkedfree两种,checked对应的debug版本,free对应release版本。

调试工具使用Softice,我使用的是2.7版本,这个工具非常强,可以看变量,内存,也可以看堆栈调用,很多棘手的问题手到擒来。

或者一般也可以通过debugview工具调试,需要的程序的关键路径上打印变量即可。当程序比较稳定了,就可以使用debugview观察逻辑上的问题,这样就可以比较快的定位,然后再使用softice进行细节调试。

 

2,  WDM模型和USB底层的实现

 

微软windows实现了usb总线的驱动,和一部分USB类驱动,通过URB封装好以后,把URB包发送给下一层的驱动,最终由总线驱动与设备通信。

WDM模型提供了USB的编程架构。这个架构包含了多个概念,包括把设备夫做到计算机上的层次方法,电源管理的通用方案,硬件的多层次描述符的自识别标准。USB标准把定时帧(frame)分解成数据包(packet),从而实现设备和主机之间的数据传输。最后,在主机和设备上的端点之间,USB支持四种数据传输模式。一种模式称为等时传输(isochronous),它可以每个一毫秒传输固定量无错误校验的数据。其他模式为:控制传输,批量传输和中段传输,它们仅能传输少量(64字节或更少)带有错误校验的数据。

USB驱动高度依赖于总线驱动程序(USBD.sys),而不直接使用HAL函数与硬件通信。它仅靠创建URB(USB请求块)并把URB提交到总线驱动程序就可以完成硬件操作。USBD.sys可以理解为接受URB的实体,向USBD的调用被转化为带有主功能代码为IRP_MJ_INTERNAL_DEVICE_CONTROLIRP。然后USBD再调度总线时间,发出URB中指定的操作。

一般我们可以按照如下的方法建立并提交一个URBUSBD驱动。

如当相应IRP_START_DEVICE消息时,首先需要读取设备描述符,然后

URB urb;

USB_DEVICE_DESCRIPTOR deviceDesc;

UsbBuildGetDescriptorRequest

         (

            &urb,

            (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),

            USB_DEVICE_DESCRIPTOR_TYPE,

            0,

            0,

            &deviceDesc,

            NULL,

            sizeof(USB_DEVICE_DESCRIPTOR),

            NULL

         );

UsbBuildGetDescriptorRequest其实是一个宏USBDLIB.H中声明用于生成读描述符请求子结构各个域。

创建完URB后,需要发一个内部IO控制(IOCTL)请求到USBD驱动程序,USBD驱动程序位于驱动程序层次结构的低端,一般需要等待设备回应。

NTSTATUS MPUSB_CallUSBD

(

   IN PDEVICE_OBJECT DeviceObject,

   IN PURB Urb

)

{

KEVENT event;

IO_STATUS_BLOCK ioStatus;

PDEVICE_EXTENSION pDevExt;

pDevExt = DeviceObject->DeviceExtension;

// issue a synchronous request to read the UTB

   KeInitializeEvent(&event, NotificationEvent, FALSE);

 

 PIRP  irp = IoBuildDeviceIoControlRequest

         (

            IOCTL_INTERNAL_USB_SUBMIT_URB,

            pDevExt -> StackDeviceObject,

            NULL,

            0,

            NULL,

            0,

            TRUE, /* INTERNAL */

            &event,

            &ioStatus

         );

 

 

   // Call the class driver to perform the operation.  If the returned status

   // is PENDING, wait for the request to complete.

 

   PIO_STACK_LOCATION  nextStack = IoGetNextIrpStackLocation( irp );

   nextStack->Parameters.Others.Argument1 = Urb;

 

      NTSTATUS ntStatus = IoCallDriver( pDevExt -> StackDeviceObject, irp );

 

      if (ntStatus == STATUS_PENDING)

      {

         ntStatus = KeWaitForSingleObject

                    (

                       &event,

                       Executive,

                       KernelMode,

                       FALSE,

                       NULL

                    );

      }

 

      // USBD maps the error code for us

      ntStatus = ioStatus.Status;

return ntStatus;

}

这样我们就可以通过USB总线与设备通信了。

当然这个过程比较复杂,需要读取并配置设备描述符,配置描述符,接口描述符和端点描述符,这些描述符在USB协议规范中有讲述,在相应的USB类规范中也有详细的描述。

一般配置描述符格式如下,

这个示例表明这个设备有两个接口,对一个接口有两个端点,第二个接口也有两个端点。每一个接口可以是一个独立的功能,这个时候设备就是复符合设备,在配置描述符中就需要声明这是个复合设备。而每一个接口也需要分别声明他的类型。

比如本人曾经有一个项目中有三个接口,分别是modem,普通串口,USB mass storage。这三个接口都需要分别声明他的类型,这些类型在USB的类规范中有详细的定义。

一般由于配置描述符的长度是不定的,所以我们需要先获取这个长字符串的前面9个字节,即配置描述符(见规范)。格式如下

typedef struct

{

   byte     length;                          // bLength: size in bytes          

   byte     descriptor_type;                 // bDescriptorType 

   word     total_length;                    // wTotalLength: in bytes

   byte     num_interfaces;                  // bNumInterfaces

   byte     configuration;                   // bConfigurationValue

   byte     config_index;                    // iConfiguration

   byte     attributes;                      // bmAttributes

   byte     max_power;                       // MaxPower: in 2mA units

} usbdc_configuration_descriptor_type;

这样,在这个字符串的第三、四字节会指明整个字符串的长度,然后再读取一次该长度,就可以完整地得到整个配置字符串。

 

1,  NDIS模型和带有WDM底层的miniport驱动

NDISNetwork Device Interface Specification)是微软的网络设备的开发库。但是由于本设备是一个USB设备,需要通过WDM模型访问。故而我们在设计时,考虑为一个NDIS miniport上层接口,带有一个WDM访问接口的驱动,很幸运,微软支持这样的设计。

 

根据设计,本驱动为一个miniport类型的驱动,所以需要开发标准的miniport接口供系统调用。和传统的驱动一样,也是封装状一系列的调用函数给系统,这些调用接口形式微软的NDIS规范已经定义好了。入口函数为DriverEntry

入口函数的功能是系统调用驱动的第一个函数,它负责初始化驱动,注册回调函数(call back)。这些函数包括如下,通过一个结构完成。

首先必须调用如下的函数,使得miniport驱动和NDIS相关联,通过调用这个函数存储miniport驱动的信息。返回的句柄NdisWrapperHandle,用于后面的回调函数注册。NDIS也通过NdisWrapperHandle来区分不同的驱动。

   NdisMInitializeWrapper(

                         &NdisWrapperHandle,

                         DriverObject,

                         RegistryPath,

                         NULL

                         );

 

注册回调函数方法如下,注册结构类型为NDIS_MINIPORT_CHARACTERISTICS

// and the entry points for driver-supplied MiniportXxx

   NdisZeroMemory(&MPChar, sizeof(MPChar));

   //

   // The NDIS version number, in addition to being included in

   // NDIS_MINIPORT_CHARACTERISTICS, must also be specified when the

   // miniport driver source code is compiled.

   //

   MPChar.MajorNdisVersion          = MP_NDIS_MAJOR_VERSION;

   MPChar.MinorNdisVersion          = MP_NDIS_MINOR_VERSION;

 

   MPChar.InitializeHandler         = MPInitialize;

   MPChar.HaltHandler               = MPHalt;

 

   MPChar.SetInformationHandler     = MPSetInformation;

   MPChar.QueryInformationHandler   = MPQueryInformation;

 

   MPChar.SendPacketsHandler        = MiniportTxPackets;

   MPChar.ReturnPacketHandler       = MiniportReturnPacket;

 

   MPChar.ResetHandler              = MPReset;

   MPChar.CheckForHangHandler       = MPCheckForHang; //optional

 

#ifdef NDIS51_MINIPORT

//   MPChar.CancelSendPacketsHandler = MPCancelSendPackets;

   MPChar.PnPEventNotifyHandler    = MPPnPEventNotify;

   MPChar.AdapterShutdownHandler   = MPShutdown;

#endif

 

NdisMRegisterMiniport(

                                 NdisWrapperHandle,

                                 &MPChar,

                                 sizeof(NDIS_MINIPORT_CHARACTERISTICS));

 

当注册好了后,系统会检测NDIS版本号,然后调用初始化接口,MPChar.InitializeHandler         = MPInitialize; 初始化驱动的其他内容,如变量的初始化,注册表的读写,事件/互斥信号量的初始化等。

如果初始化完成,那么驱动就可以正常工作了。一般通过如下接口:

   MPChar.SetInformationHandler     = MPSetInformation;

   MPChar.QueryInformationHandler   = MPQueryInformation;

进行属性的获取和设置。通过如下接口:

   MPChar.SendPacketsHandler        = MiniportTxPackets;

   MPChar.ReturnPacketHandler       = MiniportReturnPacket;

进行数据的收发。

              现在可以看到,NDIS和传统的驱动没有什么区别,但是作为网络的接口,它更加稳定,更加标准化,是开发更加简单。

 

1,  802.11协议的实现

 

1996,ETSI提出了HiperLAN的无线局域网协议,1998年日本出现HomeRFSWAP。最近几年流行的Bluetooth(严格的说, Bluetooth不属于无线局域网,而是无线PAN(Personal Area Net-work) ,应该算作无线局域网的一个子集。1997年通过的IEEE 802.11是第一种无线以太网标准,已经成为无线局域网的代名词。1999年的修订版是当前的标准主要参考。

 

802.11包括MACMedia Access Control)层和物理层

 

3种不同物理层:

DSSS or Direct Sequence Spread Spectrum

FHSS or Frequency Hopping Spread Spetrum

红线(IR or Infrared)

所以MAC层可以同时支持3种不同的物理层

 

 

基本服务集(basic service set or BSS

 

•无线局域网的最小单元,由2个或多个移动台Wireless station or STA)构成

•一个基本服务集(BSS)内的移动台(STA)间可以直接通信;

•一个基本服务集(BSS)实际上是一个自组织网络(ad hoc,802.11中称成IBSSindependent BSS

STABSS的连接是动态的,自发的(non-preplanned

扩展服务集(Extended service set or ESS

APBSS可以组成任意大的无线局域网,称作扩展服务集网络,它的所有组成部分合称扩展服务集(ESS

 

Infrastructure BSS:IBSS都称为Infrastructure BSS

 

802.11定义了9种服务类型:

移动台服务(Station Service or SS)有四种:

1.鉴别(Authentication)=who are you?

2.取消鉴别(Deauthentication)=I am about to leave, Stop communicating with me please

3.加密(privacy):数据怎能让别人偷听(Eavesdrop)

4.MSDU发送(MSDU delivery

MSDU是什么?MAC service data unit; SDU是来自(OSI)上一层的协议数据单元PDU,它定义了对下层协议的服务请求;而PDU协议数据单元(Protocol Data Unit)指对等层水平方向传送的数据单元)

 

分布系统服务(Station Service or DSS

要提供全部9种服务:

•前4种:鉴别、取消鉴别、加密、MSDU发送

•后5种:

 

5.关联(Association):它是一个DSSDistribution system service,STA如果希望将消息传输给其他BSSSTA,首先要与所在BSSAP实现关联。

6.取消关联(Disassocation):关联的反过程

7.分配(Distribution):将BSS1STA1的数据(也可以说是消息)经过DS传送到BSS2STA2,即跨DS的数据传送功能

8.集成(Integration):完成与有线网之间互通

9.再关联(Reassociation):当STABSS1移动到BSS2时,要与BSSAP实现再关联,以支持跨BSS消息传送

按照功能划分:可分为管理、控制和数据3种不同类型帧

所有MAC帧由一下3部分构成:

•帧头(MAC header

•可变长帧体(Frame body):与帧类型有关

•校验序列(frame check sequence or FCS)CRC-32

 

 

SSID说白了就是IBSSESS的一个32字节的一个标识,一个字节对应一个字符,所有可以用32个以下的字符来表示某个公司或其他组织的无线网络,类似于有线网络的工作组名字,比如我们组就叫Eda(其它字节为0

鉴别服务分为两种:

开放系统(Open System)和共享密钥(Shared Key

开放系统:如果dot11AuthenticationType被置位为1,则为开放系统鉴别,要建立鉴别,共需要2帧就可完成任务:鉴别请求,鉴别应答

共享密钥:共享密钥鉴别需要4帧。首先requester发出请求帧,然后responder发送质问(challenge)文本,该文本是WEP PRNG生产的128字节随机数,然后requester对收到的质问(challenge)文本进行WEP算法加密,responder将返回的文本解密得到相应质问文本,如果双方(responderrequester)所用密钥相同,则responder将返回的文本解密得到原始质问文本,鉴别成功

WEP加密已经被认为是不安全的算法。所以WiFi有提出了更高安全级别的算法,如WAP等。

 

MAC层功能:

1.点协调功能(PCF

2.分布协调功能(DCF

3.分段(Fragmentation

4.去分段(defragmentation)

5.MSDU重新排序和丢弃

长度大于aFramentationThresholdMSDU

MMPDUMAC管理协议数据单元)都要分段,

以提高传输成功率分段后的数据长度为某一固

定偶数(不包括最后一段)。

分段后的数据可以进行突发传送。接收端

依靠SequenceMoreFragments 域对数据进行去分段

 

 

 

•分布控制方式(DCF):多个STA争用无线信道,即所谓CSMA/CA方式(不同于CSMA/CD

•点控制方式(PCF: AP作为控制中心,控制所有STA对信道的使用。AP就是所谓Point coordinator, PCF是基于DCF的。主要用于实时业务,在802.11中是可选的。

 

随机避退时间

BackoffTime = Random()×aSlotTime

Random()--返回在[0, CW]之间均匀分布的整数

aSlotTime--物理层特性决定的CW的最小单位或称CW的切片

CW--contention window。在[aCMinaCMax] 之间取值。第一次发送时值为aCMin,

后每重传一次倍增,达到aCMax后维持aCMax。成功发送后复位到aCMin

检测到信道忙,则延迟(defer

检测到空闲且经过一个IFS,再进行

避退(Backoff)。避退时间过后如果

信道空闲,就开始发送帧

 

信标帧(Beacons