本帖主要讨论ezusb.sys的驱动的不能使用Overlapped异步模式的问题。
这会导致两个问题:
1 驱动不能即时返回,导致程序pending
2 速度上不去,使用该驱动,速度好像最高才4MB/s。
首先我们要明确一下这个问题的本质。
我先说说:
在http://bbs.zndev.com/htm_data/9/0511/102803.html中,jinghuiren大侠指出:
“
这
个是cypress驱动程序的设计缺陷,他们在设计驱动的时候只考虑演示效果,并没有考虑实时传输环境中的实际使用问题。他们在驱动程序中只实现了同步传
输的模式,也就是说在同一个时间内只能下发一个urb下去等待数据返回,数据返回后才能下发下一个urb,然而这个时间大致会有0.1s-0.2s的时
间,如果你的设备端数据产生速率较快,那么在这段时间内的产生的数据就会丢失。
解决办法,在ezusb驱动中增加异步传输的模式,在应用
程序中一次下发几个urb下去到IO管理器排队(这里没有哪0.1-0,2s的时间损耗,或者损耗很小),有数据返回后继续下发,这样能保证在高速率下不
会出现数据丢失现象,具体代码情况可以参考ddk例子中的bulkusb来修改,里面的是bulkusb_read和bulkusb_write,这是
2kddk的例子代码中的函数名,xp的有所不同,但实现基本是一样的,移植到ezusb中即可”
这让我开始思考,jinghuiren大侠说的是不是就是:该驱动没有使用StartIO呢?是不是写了StartIO就OK了呢?
另外,在Cypress公司自己的说明中又如下一段话:
you
*cannot* use the OVERLAPPED (async) mechanism, because the GPD is
strictly synchronous (blocks until the transfer > completes). You'll
have to use the multi-thread technique discussed below, or write your
own async driver.
【file:///x:/Cypress/USB/Application%20Reference
%20Materials/Support%20Information/FAQ/Main_FAQ/Driver_FAQ_Page
/Driver_FAQ_Page.htm】
应该就是jinghuiren大侠说的这个问题吧。
希望大家尽情拍砖。
我一定会把这个问题整理好,并公开全部源码……
这个driver你只要修改几个参数就可以达到28mb/s。但是数据源的数据一定是一块接着一块的,中间必须要有停顿。否则你会无法同步。
至于pending的问题的确是driver的缺陷。很容易在数据源没有准备好数据的时候造成死机。
网络上有关于在driver中实现异步传输的办法,但是这个driver实在太难调了,一不小心就是蓝屏。头大了。
我的这个用于图像采集。www.dothinkey.com
最根本的解决问题是在驱动中增加异步传输的部分
如果用同步传输,你无法在高速实时传输中保证数据不丢失,除非采用ISO模式,但ISO模式数据没有校验,会出现数据的错误现象发生,环境越差错误越多
很久没有来这里了,我也说说我的看法:
1。 cypress的驱动写的确实很好,通过阅读她的代码,才能对于cy7c68013有真正全面的认识。
2。 他的驱动是同步的不用说,我曾经尝试过多种方法,包括增加start IO历程,但是这只能够irp排队,但是不能够真正异步通讯。驱动性能没有明显提升。
3。
其实所谓异步实质也是骗人的,是将一个irp取数据分为多次实现,第一次实现一个urb定义的小包,然后返回pending,这是你的应用程序可以继续运
行(就可以释放cup了),让io管理器自动管理irp,当irp完成时,在出发一个event就可以了。最好的异步通讯例子就是ddk下的好象叫
bulkread吧(有可能记错阿)
4。 最后做完的驱动性能还是不错的,我用fpga发送8.144M*16b数据,接收稳定不断流,长时间测试没有一个bit错误。相当于除去协议开销16.288MB/S,算上协议的话,速度会更高。
5。 做到这里,算是把68013整明白了,呵呵,感谢这里的每一位帮助我的人(如jinghuiren等),非常感谢!近来比较忙,过几天我会把驱动的这段异步代码贴上来,争取能给大家带来些方便,呵呵~~
楼上的,别期待了,你去下个免费的2k ddk装上,在\NTDDK\src\wdm\usb\bulkusb目录下可以找到他说的东西
xp的类似
slave fifo 我的可以达到21Mbyte/sec
其实我目前的问题不是大家所讨论的速率的问题。这个问题跟driver关系不大,只要把 driver source
code里面的几个参数修改一下就可以达到23M/s的速度,我也曾经用30m的晶振模拟过,可以达到28M/s的目标。这对普通的数据采集或者图像采集
都是足够的。而且也很稳定。
困扰的是:
1、一次传输的量不能达到4M,
2、遇到数据不到的时候会一直pending,而无法取消。
鉴于此,就想尝试一下异步的办法,还有QJE所谓内存池轮流查询的办法。
也需要大家群策群力,一起攻克这个难题。
异步的代码:
1. 首先更改 Ezusb_ProcessIOCTL 函数中的代码
NTSTATUS
Ezusb_ProcessIOCTL(
IN PDEVICE_OBJECT fdo,
IN PIRP Irp
)
{
......
case IOCTL_EZUSB_BULK_WRITE:
case IOCTL_EZUSB_BULK_READ:
//IoMarkIrpPending(Irp);
pdx->lastStageIrp = FALSE;
Irp->IoStatus.Status = Ezusb_Read_Write(fdo,Irp); //这个函数可自己写,关键都在这个函数中
break;
......
}
2. 编写Ezusb_Read_Write函数的内容,首先初始化一堆东西,然后摄制完成例程,返回pending。
NTSTATUS
Ezusb_Read_Write(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
Return Value:
NT status code
STATUS_SUCCESS: Read was done successfully
STATUS_INVALID_PARAMETER_3: The Endpoint Index does not specify an IN pipe
STATUS_NO_MEMORY: Insufficient data memory was supplied to perform the READ
This routine fills the status code into the Irp
--*/
{
// define veriable
PMDL mdl;
PURB urb;
ULONG totalLength;
ULONG stageLength;
ULONG urbFlags;
BOOLEAN read;
NTSTATUS ntStatus;
ULONG_PTR virtualAddress;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;
PUSBD_PIPE_INFORMATION pipeInformation = NULL;
PUSBD_INTERFACE_INFORMATION interfaceInfo = NULL;
USBD_PIPE_HANDLE pipeHandle = NULL;
PBULK_TRANSFER_CONTROL bulkControl = (PBULK_TRANSFER_CONTROL)Irp->AssociatedIrp.SystemBuffer;
//
// initialize variables
//
urb = NULL;
mdl = NULL;
rwContext = NULL;
totalLength = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
read = (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
interfaceInfo = deviceExtension->Interface;
Ezusb_KdPrint(("Enter Ezusb_read()\n"));
//
// verify that the selected pipe is valid, and get a handle to it. If anything
// is wrong, return an error
//
interfaceInfo = deviceExtension->Interface;
if (!interfaceInfo)
{
Ezusb_KdPrint(("Ezusb_Read_Write() no interface info - Exiting\n"));
return STATUS_UNSUCCESSFUL;
}
if (bulkControl->pipeNum > interfaceInfo->NumberOfPipes)
{
Ezusb_KdPrint(("Ezusb_Read_Write() invalid pipe - Exiting\n"));
return STATUS_INVALID_PARAMETER;
}
pipeInformation = &(interfaceInfo->Pipes[bulkControl->pipeNum]);
if (!((pipeInformation->PipeType == UsbdPipeTypeBulk) ||
(pipeInformation->PipeType == UsbdPipeTypeInterrupt)))
{
Ezusb_KdPrint(("Ezusb_Read_Write() invalid pipe - Exiting\n"));
return STATUS_INVALID_PARAMETER;
}
pipeHandle = pipeInformation->PipeHandle;
if (!pipeHandle)
{
Ezusb_KdPrint(("Ezusb_Read_Write() invalid pipe - Exiting\n"));
return STATUS_UNSUCCESSFUL;
}
rwContext = (PBULKUSB_RW_CONTEXT)
ExAllocatePool(NonPagedPool,
sizeof(BULKUSB_RW_CONTEXT));
if(rwContext == NULL) {
Ezusb_KdPrint(("Failed to alloc mem for rwContext\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Ezusb_read_write_exit;
}
if(Irp->MdlAddress) {
totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}
if(totalLength > EZUSB_MAXIMUM_TRANSTER_SIZE) {
Ezusb_KdPrint(("Transfer length > circular buffer\n"));
ntStatus = STATUS_INVALID_PARAMETER;
ExFreePool(rwContext);
goto Ezusb_read_write_exit;
}
if(totalLength == 0) {
Ezusb_KdPrint(("Transfer data length = 0\n"));
ntStatus = STATUS_SUCCESS;
ExFreePool(rwContext);
goto Ezusb_read_write_exit;
}
urbFlags = USBD_SHORT_TRANSFER_OK;
virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);
if(read) {
urbFlags |= USBD_TRANSFER_DIRECTION_IN;
Ezusb_KdPrint(("Read operation\n"));
}
else {
urbFlags |= USBD_TRANSFER_DIRECTION_OUT;
Ezusb_KdPrint(("Write operation\n"));
}
//
// the transfer request is for totalLength.
// we can perform a max of BULKUSB_MAX_TRANSFER_SIZE
// in each stage.
//
if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = totalLength;
}
mdl = IoAllocateMdl((PVOID) virtualAddress,
totalLength,
FALSE,
FALSE,
NULL);
if(mdl == NULL) {
Ezusb_KdPrint(("Failed to alloc mem for mdl\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(rwContext);
goto Ezusb_read_write_exit;
}
//
// map the portion of user-buffer described by an mdl to another mdl
//
IoBuildPartialMdl(Irp->MdlAddress,
mdl,
(PVOID) virtualAddress,
stageLength);
urb = ExAllocatePool(NonPagedPool,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));
if(urb == NULL) {
Ezusb_KdPrint(("Failed to alloc mem for urb\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(rwContext);
IoFreeMdl(mdl);
goto Ezusb_read_write_exit;
}
UsbBuildInterruptOrBulkTransferRequest(
urb,
sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
pipeInformation->PipeHandle,
NULL,
mdl,
stageLength,
urbFlags,
NULL);
//
// set BULKUSB_RW_CONTEXT parameters.
//
rwContext->Urb = urb;
rwContext->Mdl = mdl;
rwContext->Length = totalLength - stageLength;
rwContext->Numxfer = 0;
rwContext->VirtualAddress = virtualAddress + stageLength;
rwContext->DeviceExtension = deviceExtension;
//
// use the original read/write irp as an internal device control irp
//
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = (PVOID) urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)Ezusb_ReadCompletion,
rwContext,
TRUE,
TRUE,
TRUE);
//
// since we return STATUS_PENDING call IoMarkIrpPending.
// This is the boiler plate code.
// This may cause extra overhead of an APC for the Irp completion
// but this is the correct thing to do.
//
IoMarkIrpPending(Irp);
Ezusb_KdPrint(("BulkUsb_DispatchReadWrite\n"));
// to cancel this lock, other increment to much!
//LockDevice(DeviceObject);
ntStatus = IoCallDriver(deviceExtension->StackDeviceObject,
Irp);
if(!NT_SUCCESS(ntStatus)) {
Ezusb_KdPrint(("IoCallDriver fails with status %X\n", ntStatus));
//
// if the device was yanked out, then the pipeInformation
// field is invalid.
// similarly if the request was cancelled, then we need not
// invoked reset pipe/device.
//
if((ntStatus != STATUS_CANCELLED) &&
(ntStatus != STATUS_DEVICE_NOT_CONNECTED))
{
ntStatus = Ezusb_ResetPipe(DeviceObject, 0);
}
}
//
// we return STATUS_PENDING and not the status returned by the lower layer.
//
return STATUS_PENDING;
Ezusb_read_write_exit:
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
Ezusb_KdPrint(("BulkUsb_DispatchReadWrite - ends\n"));
return ntStatus;
///////////////////
}
3. 在完成例程中判断irp是否完成,如果未完成return STATUS_MORE_PROCESSING_REQUIRED;否则,return ntStatus(success)。
NTSTATUS
Ezusb_ReadCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This is the completion routine for reads/writes
If the irp completes with success, we check if we
need to recirculate this irp for another stage of
transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
if the irp completes in error, free all memory allocs and
return the status.
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Context - context passed to the completion routine.
Return Value:
NT status value
--*/
{
ULONG stageLength;
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack;
PBULKUSB_RW_CONTEXT rwContext;
//
// initialize variables
//
rwContext = (PBULKUSB_RW_CONTEXT) Context;
ntStatus = Irp->IoStatus.Status;
UNREFERENCED_PARAMETER(DeviceObject);
Ezusb_KdPrint(("BulkUsb_ReadWriteCompletion - begins\n"));
//
// successfully performed a stageLength of transfer.
// check if we need to recirculate the irp.
//
if(NT_SUCCESS(ntStatus)) {
if(rwContext) {
rwContext->Numxfer +=
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
if(rwContext->Length) {
//
// another stage transfer
//
Ezusb_KdPrint(("Another stage transfer...\n"));
if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {
stageLength = BULKUSB_MAX_TRANSFER_SIZE;
}
else {
stageLength = rwContext->Length;
}
IoBuildPartialMdl(Irp->MdlAddress,
rwContext->Mdl,
(PVOID) rwContext->VirtualAddress,
stageLength);
//
// reinitialize the urb
//
rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength
= stageLength;
rwContext->VirtualAddress += stageLength;
rwContext->Length -= stageLength;
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.Others.Argument1 = rwContext->Urb;
nextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine(Irp,
Ezusb_ReadCompletion,
rwContext,
TRUE,
TRUE,
TRUE);
IoCallDriver(rwContext->DeviceExtension->StackDeviceObject,
Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// this is the last transfer
//
UnlockDevice(DeviceObject);
Irp->IoStatus.Information = rwContext->Numxfer;
//IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
}
}
else {
Ezusb_KdPrint(("ReadWriteCompletion - failed with status = %X\n", ntStatus));
}
if(rwContext) {
//
// dump rwContext
//
Ezusb_KdPrint(("rwContext->Urb = %X\n",
rwContext->Urb));
Ezusb_KdPrint(("rwContext->Mdl = %X\n",
rwContext->Mdl));
Ezusb_KdPrint(("rwContext->Length = %d\n",
rwContext->Length));
Ezusb_KdPrint(("rwContext->Numxfer = %d\n",
rwContext->Numxfer));
Ezusb_KdPrint(("rwContext->VirtualAddress = %X\n",
rwContext->VirtualAddress));
Ezusb_KdPrint(("rwContext->DeviceExtension = %X\n",
rwContext->DeviceExtension));
Ezusb_KdPrint(("BulkUsb_ReadWriteCompletion::"));
//LockDevice(DeviceObject);
ExFreePool(rwContext->Urb);
IoFreeMdl(rwContext->Mdl);
ExFreePool(rwContext);
}
Ezusb_KdPrint(("BulkUsb_ReadWriteCompletion - ends\n"));
return ntStatus;
}
和异步相关的代码就是这些,如果要进一步提高驱动性能,可增加startio例程与系统线程,配合异步协同工作。应用程序层面的代码,我想大家应该没有问题。
注:以上这些代码当时是在jinghuiren兄指导下调通的,在此表示感谢,并加特别说明。
oracle2046,我看了你实现了21Mbyte/s的数据率传输,请问问你是如何测得,是否有效测试数据流,以及传输是否有误。如果你愿意的话,能否贴出你的代码一看,学习一下
jinghuiren大侠:由于USB2.0的限制,一次传输的数据量最大不能超过4M,但是我们的需求有达到5M,请问如何分解并保持数据的连续性?
其实每次传输不用那么大的,用异步io的方式,每次提交几个irp去排队,每个irp要求携带64k数据量返回,这样通常不会有数据不连续的情况出现的。
你多设置几个数据缓冲区,然后多发送几个readfile请求,每个请求用不同的数据缓冲区,再设置几个overlapped标志,用GetOverlappedResult轮询几个readfile请求有没有完成,完成了收数据然后再发送一个下去,这就可以了
前提是你必须用异步地读取模式,如果是同步的readfile或deviceiocontrol是不能立刻返回的
谢谢Jinghuiren大侠的指点。
这样的话,就是说driver改成异步的方式,然后在application里面设置几个数据缓存区,多发送几个readfile请求?那这样的话如何保证到来的数据是连续的?
有些思路了,不过还是满困惑的。呵呵。谢谢。
文章评论(0条评论)
登录后参与讨论