UVC PTZ和扩展单元XU的过滤与分发调试笔记
在写这篇文章之前,我得首先感谢微软,感谢你的不严格,让我可以有空子可钻,不至于太过狼狈…
记得看过一句话,开局一张图,内容全告编。我多么希望自己在UVC摄像头的调试过程中也可以瞎编乱造,这样不至于身心疲惫。至少保持着愉快的心情,吹吹牛,也是一件很开心的事。
今日有一个需求,对UVC摄像头的请求进行过滤和重新分发。主要的内容是对摄像头的数据帧格式进行过滤篡改和对UVC摄像头如PTZ和扩展单元的特定类请求进行过部分的过滤和重新分发。
注意这里的部分两个字,比如说对UVC扩展单元支持的10个选择子,5个选择子要直接转发给固件,另外5个要截获交给应用层来处理(其实说成对扩展单元的选择子扩展也许更好)。
我准备了两个方案:
- 第一是自己重新实现USBCCGP.SYS功能,使用自己编写的驱动代替微软的USBCCGP.SYS,这样可以在该驱动中分析固件的配置描述符,分别虚拟其相关的设备。如UVC摄像头,HID设备和UAC设备。
- 第二个也是重新实现USBCCGP.SYS功能,不过对大部分请求进行重新转发,只对自己感兴趣的请求进行过滤转发。但是由于数据是需要在应用层转一圈再回来,所以得自己做一些关联IRP请求。不过这个本人并没有实现过,也只是在reactos源代码中看到过(http://www.pnpon.com/article/detail-175.html )。
讲道理,按理来说第二种方法实现起来可能比较简单,但实际上我选的是第一种方案。
为什么选择第一种方案呢?原理是比较简单的,防止需求的变化。因为作为一各程序员,最怕的是需求的变化,最恐怖的是发现在自己以前的方案不能满足新的需求或改起来特别复杂。自己都不一定能改正确。
相机终端PTZ请求
在通过虚拟各种设备成功后,编写其数据的交互过程。在UVC的特定类请求时,卡住了…
由于要对PTZ功能进行过滤处理,首先得将上层发给驱动设备的特定为请求进行截获转发。截获到还可以,问题就出现在了转发。由于我们是不同的设备,且自己虚拟的相机终端单元信息和固件的中的不一致,没办法为了少写转化代码。自己尽可能将如ID等拓扑信息与固件保持一致。在第一次下发请求时,出现了问题:
抓包信息如下:
13.0 CTL a1 86 00 0b 00 01 01 00 CLASS
13.0 USTS c0000004 stall pid
13.0 CTL a1 86 00 0c 00 01 01 00 CLASS
13.0 USTS c0000004 stall pid
13.0 CTL a1 86 00 0d 00 01 01 00 CLASS
13.0 USTS c0000004 stall pid
13.0 CTL a1 86 00 0e 00 01 01 00 CLASS
13.0 USTS c0000004 stall pid
13.0 CTL 21 0a 00 00 00 00 00 00 SET IDLE
13.0 USTS c0000004 stall pid
PID错误,关于PID的内容见:http://www.usbzh.com/article/detail-73.html
当时我就懵了,这玩意还到了USB协议层,幸好本人与正确的进行抓包对比,机智地发现由于接口ID的不同(自己虚拟是从0开始的,但在固件中0是HID设备,从1接口开发UVC).
31.0 CTL a1 86 00 0b 01 01 01 00 GET INFO
31.0 1 IN 13 .
31.0 CTL a1 82 00 0b 01 01 02 00 GET MIN
31.0 2 IN 00 00 ..
31.0 CTL a1 83 00 0b 01 01 02 00 GET MAX
31.0 2 IN d0 02 ..
31.0 CTL a1 84 00 0b 01 01 02 00 GET RES
31.0 2 IN 01 00 ..
31.0 CTL a1 87 00 0b 01 01 02 00 GET DEF
31.0 2 IN 00 00 ..
31.0 CTL a1 86 00 0c 01 01 01 00 GET INFO
31.0 1 IN 03 .
31.0 CTL a1 82 00 0c 01 01 03 00 GET MIN
31.0 3 IN 00 00 00 ...
31.0 CTL a1 83 00 0c 01 01 03 00 GET MAX
31.0 3 IN 00 00 01 ...
31.0 CTL a1 84 00 0c 01 01 03 00 GET RES
31.0 3 IN 00 00 01 ...
31.0 CTL a1 87 00 0c 01 01 03 00 GET DEF
31.0 3 IN 00 00 01 ...
所以对于相机终端的特定类请求(http://www.usbzh.com/article/detail-47.html ),我是这样做的:
if (Urb->UrbControlVendorClassRequest.Index == 0x0100)
{
Urb->UrbControlVendorClassRequest.Index += 0x01;
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS Status = IoCallDriver(deviceExtension->PhysicalDevice, Irp);
Urb->UrbControlVendorClassRequest.Index -= 0x01;
return Status;
}
扩展单元请求
当实现了相机终端的请求,扩展单元还不是一件直分简单的事…
可当我加上之后,发现应用层的有问题了,各种卡死。。。甚至应用层崩掉
就是因为发了这些特定类请求PTZ和扩展单元请求才出的问题。。
当我又改回去不支持扩展单元时,只有相机的PTZ时,又好了。。。。
这…..
难道我的代码有问题,有耦合,难道固件的扩展单元和PTZ有偶合….
当出现这种问题时,也现了大量的IRP cancel,这难道是IRP没有处理的问题,抓包发现IRP有时PENDING…
不显示IN包的抓包信息:
15.0 CTL a1 85 00 02 01 0b 02 00 CLASS
15.0 CTL a1 81 00 02 01 0b 01 00 CLASS
15.0 CTL a1 81 00 0e 01 01 04 00 CLASS
15.0 CTL a1 85 00 02 01 0b 02 00 CLASS
15.0 CTL a1 81 00 0e 01 01 04 00 CLASS
15.0 USTS c0000004 stall pid
15.0 USTS c0000004 stall pid
各种偿试了…
突然想到一个问题,就是当自己下发IRP前,将Index += 0x01;然后调用IoCallDriver,但是调用完成后又将Index -= 0x01了,但如果遇到pending的状态,由于自己将Index又减回去了,所以有可能是当需要处理的时候,Index不正确了所导致的问题…
为了验证这个这问题,本人将代码这样改了一下:
if (Urb->UrbControlVendorClassRequest.Index == 0x0100)
{
Urb->UrbControlVendorClassRequest.Index |= 0x01;
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS Status = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
// Urb->UrbControlVendorClassRequest.Index -= 0x01;
return Status;
}
else if (Urb->UrbControlVendorClassRequest.Index == 0x0B00) //
{
Urb->UrbControlVendorClassRequest.Index |= 0x01;
IoSkipCurrentIrpStackLocation(Irp);
NTSTATUS Status = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp);
if (Status == STATUS_PENDING)
{
// KdBreakPoint();
}
// Urb->UrbControlVendorClassRequest.Index -= 0x01;
return Status;
}
结果是:还真是这个问题…
下来其实最好的方法是对该IRP设置完成例程,在完成例程中修改回去。但是一般当IRP完成后,不是会校验这类输入参数的,所以好像这样也没啥问题…
偷个懒…