+ -

USB 等时/同步传输、块传输与转换设置在UVC摄像头驱动中的探讨

欢迎加入字节流官方QQ群:952873936或联系站长进行技术交流。

最近在Windows10 x64环境下,开发了一个虚拟UVC摄像头驱动。确切的来说这不是摄像头驱动,而是一个虚拟USB总线驱动。使用该虚拟总线驱动使用应用软件通过IOCTL控制总线子设备的创建与卸载。

框架设计

驱动安装完成后,是一个单纯的USB虚拟总线。

应用软件通过发送自定义IOCTL码IOCTL_BUSENUM_PLUGIN_CAMERA通知驱动程序。
驱动程序收到IOCTL_BUSENUM_PLUGIN_CAMERA请求后,使用IoCreateDevice创建一个PDO,并挂入子设备链表,然后使用IoInvalidateDeviceRelations通过PNP管理器该问线子设备有变化。
后续就是一堆的子设备的枚举,详细的过程可见 windows加载即插即用PNP设备的过程 一节。

言归正转,使用该虚拟总线驱动枚举出一个子设备后,子设备会加载USB通用驱动USBCCGP,再由USBCCGP枚举出UVC Camera设备。其总线关系如下图所示:

本人的测试机为英文操作系统

  • Virtula USB Bus 我们开发的虚拟总线驱动vusbbus.sys
    • USB Compoite Device - USB通用驱动usbccgp.sys
      • USB Cpature - USB摄像头驱动 ksthunksys和usbvideo.sys

总线关系

UVC描述符布局

批量传输

参考UVC1.5描述符相关章节.本人的配置描述符布局如下:
这里只写重要的描述符。

  • 配置描述符
  • 接口关联描述符
    • 接口描述符 bInterfaceNunber = 0,bAlternateSetting=0
    • 类特定视频控制接口头描述符
    • 接口描述符 bInterfaceNunber = 1,bAlternateSetting=0
    • 类特定视频控制接口头描述符
    • 端点描术符 bulk,bEndpointAddress=0x82,wMaxPacketSize=4000h

可以看到,在这里使视频流的传输使用了0x82地址,最大传输字节为16KB,使用的是块传输。
在使用过程中,发现会出现一个问题,就是上层打开摄像头时,而我们通过我们的开发的应用软件很难有一个好的方法获取当前摄像头的是否处于打开状态,这样很难报告上层应用软件是否需要写数据。

等时/同步传输

在原来来块传输的基础下,修改配置描述符如下:

  • 配置描述符
  • 接口关联描述符
    • 接口描述符 bInterfaceNunber = 0,bAlternateSetting=0
    • 类特定视频控制接口头描述符
    • 接口描述符 bInterfaceNunber = 1,bAlternateSetting=0
    • 类特定视频控制接口头描述符

    • 接口描述符 bInterfaceNunber = 1,bAlternateSetting=1
    • 端点描术符 异步传输,bEndpointAddress=0x82,wMaxPacketSize=400h

这里将原来的接口1中的端点个数设为0,并新增加一个其转换设置为1的接口描述符,在其内部再原来的块/批量传输改为等时传输,并需要修改wMaxPacketSize=400。这是因为在等时传输的包(高速、全速)的最大上限为1024个字节,否则虽然会枚举成功,但打开摄像头时会报说设备被占用的错误导致打开失败。

关于等时传输的包大小:

等时/同步传输的包大小最大1024字节,是指每次的传输,但每个URB中可以包含多个包传输。
如以WINDOWS下URB的等进传输URB结构体定义如下:

struct _URB_ISOCH_TRANSFER {
    struct _URB_HEADER Hdr;
    USBD_PIPE_HANDLE PipeHandle;
    ULONG TransferFlags;
    ULONG TransferBufferLength;
    PVOID TransferBuffer;
    PMDL TransferBufferMDL;
    struct _URB *UrbLink; // Reserved
    struct _URB_HCD_AREA hca; // Reserved
    ULONG StartFrame;
    ULONG NumberOfPackets;
    ULONG ErrorCount;
    USBD_ISO_PACKET_DESCRIPTOR IsoPacket[1];
};
typedef struct _USBD_ISO_PACKET_DESCRIPTOR {
    ULONG Offset;
    ULONG Length;
    USBD_STATUS Status;
} USBD_ISO_PACKET_DESCRIPTOR, *PUSBD_ISO_PACKET_DESCRIPTOR;

这里NumberOfPackets代表可以该URB可以有多少个数据包。如将最大设为1024,则Offset为该 USBD_ISO_PACKET_DESCRIPTOR的数据偏移,为1024的整数包,Length为该包的长度,前面几包为1024,最后一包小于等于1024字节。

这里有一个比较奇怪的问题就是,如果新的接口1中的端点类型设为块传输,但上层依旧会依等时/同步传输的方式来读取数据。可能这是因为是在转换设置接口中的吧。

关于摄像头是否打开的判断

通过上层选择接口中其转换设置的不同,可以判断当前摄像头的状态。

  • 如转换设置为0,表示关闭或初始化摄像头。
  • 如转换设置为1,表示打开摄像头。

我见过的UVC摄像头都是使用批量传输的方式进行数据读取的,详见 UVC 摄像头的打开与关闭 一节详细的特定类请求。

微信公众号
字节流公众号  字节流QQ群:952873936
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

Powered by bytekits.com,汇天下文字,成非凡梦想!!!