WinXP下USB驱动开发(五)

3.3.2.  程序设计

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

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

3.3.2.1.    数据结构

3.3.2.1.1. 设备描述符

每个设备都有一个唯一的设备描述符,它向主机软件标识该设备。主机使用GET_DESCRIPTOR控制事务直接从设备的0号端点读取该描述符。该描述符在DDK中的定义如下:

typedef struct _USB_DEVICE_DESCRIPTOR {

    UCHAR bLength;

    UCHAR bDescriptorType;

    USHORT bcdUSB;

    UCHAR bDeviceClass;

    UCHAR bDeviceSubClass;

    UCHAR bDeviceProtocol;

    UCHAR bMaxPacketSize0;

    USHORT idVendor;

    USHORT idProduct;

    USHORT bcdDevice;

    UCHAR iManufacturer;

    UCHAR iProduct;

    UCHAR iSerialNumber;

    UCHAR bNumConfigurations;

} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;

Ø       设备描述符的bLength域应等于1

Ø       bDescriptorType域应等于1以指出该结构是一个设备描述符。

Ø       bcdUSB域包含该描述符遵循的USB规范的版本号(BCD编码)。现在,设备可以使用值0x01000x0110来指出它所遵循的是1.0版本还是1.1版本的USB规范。

Ø       bDeviceClassbDeviceSubClassbDeviceProtocol指出设备类型。可能的设备类代码在USB规范中定义,本书所包括的类代码在表4-0 中列出。USB委员会的独立设备类工作组为每个设备类定义子类和协议代码。例如,音频类有控制、流,和MIDI流接口的子类代码。大容量存储类为使用各种端点的数据传输方法定义了协议代码。

Ø       设备描述符的bMaxPacketSize0域给出了默认控制端点(端点0)上的数据包容量的最大值。每个设备都必须提供0号控制端点,由于USB规范并没有为该端点规定一个单独的端点描述符,所以这个域是唯一描述这个端点的地方。因为这个域在设备描述符的偏移7处,所以即使该端点使用最小的传输容量(8字节)主机也能读到这个域。一旦主机知道了端点0的最大传输容量,它就可以分块读出整个描述符。

Ø       idVendoridProduct域指定厂商代码和厂商专用的产品标识。

Ø       bcdDevice指出设备的发行版本号(0x0100对应版本1.0)

Ø       iManufactureriProduct、和iSerialNumber域指向一个串描述符,该串描述符用人类可读的语言描述设备生产厂商、产品、和序列号。这些串是可选的,0值代表没有描述串。如果你在设备上放入了序列号串,Microsoft建议应使每个物理设备的序列号唯一。

Ø       bNumConfigurations指出该设备能实现多少种配置。Microsoft的驱动程序仅工作于设备的第一种配置(1号配置)。我将在后面解释怎样使用设备的多个配置。

4-0 USB设备类代码

符号名

类代码

描述

USB_DEVICE_CLASS_RESERVED

0

指出类代码存在于接口描述符中

USB_DEVICE_CLASS_AUDIO

1

操作模拟或数字音频、语音、和其它与声音相关的数字设备

USB_DEVICE_CLASS_COMMUNICATIONS

2

电讯设备,如调制解调器、电话、应答机,等等

USB_DEVICE_CLASS_HUMAN_INTERFACE

3

人类接口设备,如键盘、鼠标、麦克风,等等

USB_DEVICE_CLASS_MONITOR

4

显示器

USB_DEVICE_CLASS_PHYSICAL_INTERFACE

5

含有实时物理反馈的人类接口设备,如力反馈游戏杆

USB_DEVICE_CLASS_POWER

6

执行电源管理的人类接口设备,如电池、充电器,等等

USB_DEVICE_CLASS_PRINTER

7

打印机

USB_DEVICE_CLASS_STORAGE

8

大容量存储设备,如磁盘和CD-ROM

USB_DEVICE_CLASS_HUB

9

USB hubs

USB_DEVICE_CLASS_VENDOR_SPECIFIC

255

厂商定义的设备类

3.3.2.1.2. 配置描述符

每个设备有一个或多个配置描述符,它们描述了设备能实行的各种配置方式。DDK中定义的配置描述符结构如下:

typedef struct _USB_CONFIGURATION_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    USHORT wTotalLength;
    UCHAR bNumInterfaces;
    UCHAR bConfigurationValue;
    UCHAR iConfiguration;
    UCHAR bmAttributes;
    UCHAR MaxPower;
} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;

Ø       bLengthbDescriptorType域应为92,即是一个9字节长的配置描述符。

Ø       wTotalLength域为该配置描述符长度加上该配置内所有接口和端点描述符长度的总和。

Ø       bNumInterfaces指出该配置有多少个接口。这个值仅是接口的数量,不包括接口中的替换设置。这个域的目的是允许多功能设备存在,如一个有定位器(类似于鼠标)的键盘。

Ø       bConfigurationValue域是该配置的索引值。你可以用这个值在SET_CONFIGURATION控制请求中选择这个配置。注意设备的第一个配置描述符的索引为1(选择配置0将把设备置入未配置状态,此时仅有端点0是活动的)

Ø       iConfiguration域是一个可选的串描述符索引,指向描述该配置的Unicode字符串。此值为0表明该配置没有串描述符。

Ø       bmAttributes字节包含描述该配置中设备电源和其它特性的的位掩码,如图4-1所示。

Ø       MaxPower域中指出要从USB总线上获取的最大电流量(单位为2mA)

4-1 配置的属性位

位掩码

符号名称

描述

80h

USB_CONFIG_BUS_POWERED

废弃 -- 应总为1

40h

USB_CONFIG_SELF_POWERED

该配置为自供电

20h

USB_CONFIG_REMOTE_WAKEUP

该配置有远程唤醒特征

3.3.2.1.3. 接口描述符

每个配置有一个或多个接口描述符,它们描述了设备提供功能的接口。DDK中的接口描述符结构定义如下:

typedef struct _USB_INTERFACE_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    UCHAR bInterfaceNumber;
    UCHAR bAlternateSetting;
    UCHAR bNumEndpoints;
    UCHAR bInterfaceClass;
    UCHAR bInterfaceSubClass;
    UCHAR bInterfaceProtocol;
    UCHAR iInterface;
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;

Ø       bLengthbDescriptorType域应为94

Ø       bInterfaceNumberbAlternateSetting是索引值,用在SET_INTERFACE控制事务中以指定要激活的接口。这些值可以是任意的,但习惯上,配置中的接口号从0开始,每个接口中的替换设置也是从0开始的。

Ø       bNumEndpoints域指出该接口有多少个端点,不包括端点0,端点0被认为是总存在的,并且是接口的一部分。

Ø       bInterfaceClassbInterfaceSubClass、和bInterfaceProtocol域描述了接口提供的功能。一个非0的类代码应该是上面讨论的类代码中的一个,同时子类和协议代码也必须有与该类相类似的含义。这些域不允许有0值,0值为未来标准保留。

Ø       iInterface是一个串描述符的索引,0表示该接口无描述串。

3.3.2.1.4. 端点描述符

接口可以没有或有多个端点描述符,它们描述了处理事务的端点。DDK中定义的端点描述符结构如下:

typedef struct _USB_ENDPOINT_DESCRIPTOR {
    UCHAR bLength;
    UCHAR bDescriptorType;
    UCHAR bEndpointAddress;
    UCHAR bmAttributes;
    USHORT wMaxPacketSize;
    UCHAR bInterval;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;

Ø       bLengthbDescriptorType域应为75

Ø       bEndpointAddress域编码端点的方向性和端点号,如图4-3-1-0所示。例如,地址值0x82指出该端点是一个端点号为2IN端点,而0x02地址指出一个端点号为2OUT端点。除了端点0,两个端点可以有相同的端点号但方向相反。

Ø       bmAttributes的低两位指出端点的类型,如表4-1所示。

4-1. 端点类型代码

符号名称

端点类型

USB_ENDPOINT_TYPE_CONTROL

0

控制端点

USB_ENDPOINT_TYPE_ISOCHRONOUS

1

等时端点

USB_ENDPOINT_TYPE_BULK

2

批量端点

USB_ENDPOINT_TYPE_INTERRUPT

3

中断端点

3.3.2.1.5. 串描述符

设备、配置、端点描述符都可以包含一个指向人类可读串的指针。串本身以USB串描述符的形式保存在设备中,串字符使用Unicode编码。DDK中的串描述符结构声明如下:

typedef struct _USB_STRING_DESCRIPTOR {

    UCHAR bLength;

    UCHAR bDescriptorType;

    WCHAR bString[1];

} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR;

Ø       bLength值根据串数据长度可变。

Ø       bDescriptorType域的值应为3

Ø       bString域包含串数据本身。

3.3.2.1.6. 通道结构描述

管道信息结构是我们在此真正关心的,因为结构中的其它域是在提交URB后由USBD填充的。管道结构描述如下:

typedef struct _USBD_PIPE_INFORMATION {
  USHORT MaximumPacketSize;
  UCHAR EndpointAddress;
  UCHAR Interval;
  USBD_PIPE_TYPE PipeType;
  USBD_PIPE_HANDLE PipeHandle;
  ULONG MaximumTransferSize;
  ULONG PipeFlags;
} USBD_PIPE_INFORMATION, *PUSBD_PIPE_INFORMATION;

Ø       MaximumPacketSize域表示该指定的通道能够传输的最大包。

Ø       EndpointAddress域表示 8位的端口地址

Ø       如果该通道为中断传输通道,Interval表示延时时间,以ms计算。

Ø       PipeType表示通道类型,为控制、同步、批量、等时四种。

Ø       MaximumTransferSizePipeFlags分别表示一次请求的最大数据长度和通道的开启状态。

3.3.2.2.    构造URB

3.3.2.2.1. URB结构

URBWDM功能驱动和USBD.SYS总线驱动通讯的桥梁,功能驱动中所有对总线的操作都需要翻译成一个特定的URB。因此构造URB是功能驱动最主要的任务,URB其实只是操作众多操作的集合,其结构体描述如下:

typedef struct _URB {

  union {

  struct _URB_HEADER  UrbHeader;

  struct _URB_SELECT_INTERFACE  UrbSelectInterface;

  struct _URB_SELECT_CONFIGURATION  UrbSelectConfiguration;

  struct _URB_PIPE_REQUEST  UrbPipeRequest;

  struct _URB_FRAME_LENGTH_CONTROL  UrbFrameLengthControl;

  struct _URB_GET_FRAME_LENGTH  UrbGetFrameLength;

  struct _URB_SET_FRAME_LENGTH  UrbSetFrameLength;

  struct _URB_GET_CURRENT_FRAME_NUMBER 

UrbGetCurrentFrameNumber;

  struct _URB_CONTROL_TRANSFER  UrbControlTransfer;

  struct _URB_BULK_OR_INTERRUPT_TRANSFER 

UrbBulkOrInterruptTransfer;

  struct _URB_ISOCH_TRANSFER  UrbIsochronousTransfer;

  struct _URB_CONTROL_DESCRIPTOR_REQUEST 

UrbControlDescriptorRequest;

  struct _URB_CONTROL_GET_STATUS_REQUEST 

UrbControlGetStatusRequest;

  struct _URB_CONTROL_FEATURE_REQUEST  UrbControlFeatureRequest;

  struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST 

UrbControlVendorClassRequest;

  struct _URB_CONTROL_GET_INTERFACE_REQUEST 

UrbControlGetInterfaceRequest;

  struct _URB_CONTROL_GET_CONFIGURATION_REQUEST 

UrbControlGetConfigurationRequest;

  }

} URB, *PURB ;

Ø       UrbHeaderUSB总线操作需要的基本信息,该域由系统自动构建,一般不做特殊处理。

Ø       UrbSelectInterfaceUSB设备选择接口的结构体。

Ø       UrbSelectConfigurationUSB设备选择配置的结构体。

Ø       UrbPipeRequest为通道(pipe)重新启用的结构体。

Ø       UrbFrameLengthControl 为设定总线帧长度,如果无须设置可以由系统自动设置。

Ø       UrbGetFrameLength获取现在总线帧长度。

Ø       UrbSetFrameLength设定总线帧长度。

Ø       UrbGetCurrentFrameNumber获取总线现今帧数量。

Ø       UrbControlTransfer启用控制端口传输和接收数据。

Ø       UrbBulkOrInterruptTransfer启用批量传输端口传输和接收数据,或者采用中断端口接收数据。

Ø       UrbIsochronousTransfer启用等时端口接收和传输数据。

Ø       UrbControlDescriptorRequest用于接收和设置描述符。

Ø       UrbControlGetStatusRequest用于获取设备、接口和端点的状态。

Ø       UrbControlFeatureRequest用于设置或清除设备、接口和端点的特性。

Ø       UrbControlVendorClassRequest用于发送和接收设备、接口和端点的vendor or class-specific请求。

Ø       UrbControlGetInterfaceRequest用于获取当前接口的alternate设置。

Ø       UrbControlGetConfigurationRequest用于获取设备当前的配置。

3.3.2.2.2. UsbBuildInterruptOrBulkTransferRequest

构造一个URB用于发送和接收数据,其中批量传输端点可以采用批量方式进行传输和接收,中断端点只能接收从机发出的数据。函数描述如下:

VOID

  UsbBuildGetInterruptOrBulkTransferRequest(

    IN OUT PURB  Urb,                    

IN USHORT  Length,                  

IN USBD_PIPE_HANDLE  PipeHandle,

    IN PVOID  TransferBuffer  OPTIONAL,

    IN PMDL  TransferBufferMDL  OPTIONAL,

    IN ULONG  TransferBufferLength,

    IN ULONG  TransferFlags,

    IN PURB  Link

    );

Ø       Urb为指定构造的URB结构体。

Ø       Length构造的特定类型URB结构体的长度,一般为sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),该域主要用于帮助USBD.SYS判断需要构造的是那种类型的URB,    其中采用Switch(…)case{…}结构进行处理。

Ø       PipeHandle管道句柄。

Ø       如果数据缓冲方式为直接存取,则TransferBufferMDL为需要存取的MDL结构体,TransferBuffer NULL,反之亦然。

Ø       TransferFlagsUSB信息传输包的方向标识,一包有USBD_TRANSFER_DIRECTION_INUSBD_SHORT_TRANSFER_OKUSBD_TRANSFER_DIRECTION_OUT三种。

Ø       Linkcaller-initialized URB指针,一般设置为NULL

3.3.2.2.3. UsbBuildGetDescriptorRequest

构造一个URB用于获取设备配置信息,其中获取的URB是可以指定的,可以同时获取设备描述信息、接口描述信息和所有端点描述信息。该函数描述如下:

VOID

  UsbBuildGetDescriptorRequest(

    IN OUT PURB  Urb,

    IN USHORT  Length,

    IN UCHAR  DescriptorType,

    IN UCHAR  Index,

    IN USHORT  LanguageId,

    IN PVOID  TransferBuffer  OPTIONAL,

    IN PMDL  TransferBufferMDL  OPTIONAL,

    IN ULONG  TransferBufferLength,

    IN PURB  Link  OPTIONAL);

该函数参数和UsbBuildInterruptOrBulkTransferRequest函数参数含义基本一致,但也存在细微的差别。其中DescriptorType指定描述符类型,USB_DEVICE_DESCRIPTOR_TYPEUSB_CONFIGURATION_DESCRIPTOR_TYPEUSB_STRING_DESCRIPTOR_TYPE三种。并且TransferBufferLength参数不同也决定了获取的USB描述信息的不同,例如为sizeof(USB_CONFIGURATION_DESCRIPTOR)时,获取的这是描述符;如果为configurationDescriptor->wTotalLength[1]时,获取的就是描述符、接口描述符和所有端点描述信息,在IRP_MN_START_DEVICE函数例程中进行USB配置时,需要选择获取描述符信息,后获取接口和所有端点描述信息,最后配置端点和接口信息。

1 #define CONFIG_DESCRIPTOR_LENGTH

sizeof(USB_CONFIGURATION_DESCRIPTOR) /

+ sizeof(USB_INTERFACE_DESCRIPTOR) /

+ (NUM_ENDPOINTS(端点数) * sizeof(USB_ENDPOINT_DESCRIPTOR))

3.3.2.2.4. UsbBuildSelectConfigurationRequest

构造一个URB用于为设备选择一个配置描述符,该函数描述如下:

VOID

  UsbBuildSelectConfigurationRequest(

    IN PURB  Urb,

    IN USHORT  Length,

    IN PUSB_CONFIGURATION_DESCRIPTOR  ConfigurationDescriptor);

Ø       Urb为需要构建的URB结构体,Length_URB_SELECT_CONFIGURATION结构体的长度利用sizeof(_URB_SELECT_CONFIGURATION)获得。

Ø       ConfigurationDescriptor为初始化USB描述符指针,如果该值为NULL,该函数将造成设备为未配置。在XxxUnload例程中需要调用这个函数,且使ConfigurationDescriptor参数为NULL

你可能感兴趣的:(struct,Microsoft,Class,interface,Descriptor,DDK)