Window XP驱动开发(十三) 芯片功能驱动端 (代码实现,针对USB2.0 芯片CY7C68013A)

2023-11-19

 

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家提出意见,一起讨论!

需要源码的可以与我联系.

 

 

针对USB2.0 芯片CY7C68013A+FPGA实现的高速传输应用来写XP下的USB驱动程序。

说明:

1、供应商驱动

 其实在此芯片供应商CYPRESS的网站上已经可以获得针对它的XP下功能驱动的 .sys .inf 文件,(可以到这里下载http://www.cypress.com/?rID=34870

下载后得到

 

安装后得到驱动安装文件:

 

2、本博客目的

 

由于供应商没有提供驱动源码,但为了让自己更明白驱动的开发过程,我们还是在这里自己开发功能驱动层,仅作为学习用。

真实的应用还是用供应商的驱动。

 

 3、测试过程

第4步我们给出了功能驱动的源码,假设我们已经得到了ezusbdrv.sys 文件。

接下来我们来测试它是否可用:

(1)编写inf文件。

由于是刚买回来的连接到CY7C8013的EEPROM里没有东西,这样CY7C8013里的增加型8051的代码不能从EEPROM中获得,于是CY7C8013只能从内核启动。

所以得到的PID与VID是默认设置的,即VID:04B4           PID:8613

这里告诉大家一个方法来知道连接到PC的USB设备的VID与PID:

在设备管理器中查看连接的USB设备,即使是没有加载驱动的设备,当你右击设备->属性时,会看到

这里看到的VID与PID是USB总线从CY7C中请求设备信息时得到的。

根据VID与PID我们写出ezusbdrv.inf文件:

; Installation INF for the Cypress Generic USB Driver for Windows XP
; Processor support for x86 based platforms.
;
; (c) Copyright 2011 Cypress Semiconductor Corporation
;

[Version]
Signature="$WINDOWS NT$"
Class=USB
ClassGUID={36FC9E60-C465-11CF-8056-444553540000}
provider=%EZUSBDRV_Provider%
CatalogFile=CYUSB.cat
DriverVer=10/17/2012,3.4.7.027

[SourceDisksNames]
1=%EZUSBDRV%,,,

[SourceDisksFiles]
ezusbdrv.sys = 1

[DestinationDirs]
CYUSB.Files.Ext = 10,System32\Drivers

[ControlFlags]
ExcludeFromSelect = *

[Manufacturer]
%EZUSBDRV_Provider%=Device,NT,NTx86,NTamd64

;for all platforms
[Device]
%VID_04B4&PID_8613.DeviceDesc%=CyUsb, USB\VID_04B4&PID_8613


;for windows 2000 non intel platforms
[Device.NT]
%VID_04B4&PID_8613.DeviceDesc%=CyUsb, USB\VID_04B4&PID_8613


;for x86 platforms
[Device.NTx86]
%VID_04B4&PID_8613.DeviceDesc%=CyUsb, USB\VID_04B4&PID_8613


;for x64 platforms
[Device.NTamd64]
%VID_04B4&PID_8613.DeviceDesc%=CyUsb, USB\VID_04B4&PID_8613


[CYUSB]
CopyFiles=CYUSB.Files.Ext
AddReg=CyUsb.AddReg

[CYUSB.HW]
AddReg=CYUSB.AddReg.Guid

[CYUSB.Services]
Addservice = CYUSB,2,CYUSB.AddService

[CYUSB.NT]
CopyFiles=CYUSB.Files.Ext
AddReg=CyUsb.AddReg

[CYUSB.NT.HW]
AddReg=CYUSB.AddReg.Guid

[CYUSB.NT.Services]
Addservice = CYUSB,2,CYUSB.AddService


[CYUSB.NTx86]
CopyFiles=CYUSB.Files.Ext
AddReg=CyUsb.AddReg

[CYUSB.NTx86.HW]
AddReg=CYUSB.AddReg.Guid

[CYUSB.NTx86.Services]
Addservice = CYUSB,2,CYUSB.AddService

[CYUSB.NTamd64]
CopyFiles=CYUSB.Files.Ext
AddReg=CyUsb.AddReg

[CYUSB.NTamd64.HW]
AddReg=CYUSB.AddReg.Guid

[CYUSB.NTamd64.Services]
Addservice = CYUSB,2,CYUSB.AddService


[CYUSB.AddReg]
; Deprecating - do not use in new apps to identify a CYUSB driver
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,ezusbdrv.sys
; You may optionally include a check for DriverBase in your application to check for a CYUSB driver
HKR,,DriverBase,,ezusbdrv.sys
HKR,"Parameters","MaximumTransferSize",0x10001,4096
HKR,"Parameters","DebugLevel",0x10001,2
HKR,,FriendlyName,,%EZUSBDRV_Description%

[CYUSB.AddService]
DisplayName    = %EZUSBDRV_Description%
ServiceType    = 1                  ; SERVICE_KERNEL_DRIVER
StartType      = 3                  ; SERVICE_DEMAND_START
ErrorControl   = 1                  ; SERVICE_ERROR_NORMAL
ServiceBinary  = %10%\System32\Drivers\ezusbdrv.sys
AddReg         = CYUSB.AddReg
LoadOrderGroup = Base

[CYUSB.Files.Ext]
ezusbdrv.sys

[CYUSB.AddReg.Guid]
HKR,,DriverGUID,,%EZUSBDRV.GUID%
HKR,,DriverEXECSCRIPT,,%EZUSBDRV.EXECSCRIPT%

[Strings]
EZUSBDRV_Provider    = "EZUSBDRV"
CYUSB_Company     = "Cypress Semiconductor Corporation"
EZUSBDRV_Description = "Cypress Generic USB Driver"
CYUSB_DisplayName = "Cypress USB Generic"
EZUSBDRV_Install     = "Cypress CYUSB Driver Installation Disk"
VID_04B4&PID_8613.DeviceDesc="Cypress USB Generic Driver ezusbdrv(3.4.7.000)"
EZUSBDRV.GUID="{AE18AA60-7F6A-11d4-97DD-00010229B969}"
EZUSBDRV_Unused      = "."
EZUSBDRV.EXECSCRIPT="\systemroot\system32\MyDevice\MyDevice.spt"


(2) 此时插入USB,由于没有驱动,会在其它设备下显示“USB device"。

右击“USB Device",更新驱动程序,选择“从列表或指定位置安装”

找到我们的ezusbdrv.sys 与 ezusbdrv.inf所在的文件夹。

i386下有ezusbdrv.inf和ezusbdrv.sys文件。

点下一步时,系统会到i386目录下找所有的.inf文件,并根据.inf里的VID、PID与自己获得的VID、PID比较,

如果发现一样,那么就按这个inf文件来安装驱动

从上图大家可以看到,如果找到匹配的驱动,系统会把此驱动文件拷贝到C:\WINDOWS\system32\drivers下。

这样下次你再插入此设备时,会自动安装。

从下图中看出我们的驱动安装成功了:

 

(3) 解释.inf文件节点的在驱动安装中的真实意义

(3、1)在设备管理器中显示的名字

(3、2) 设备类型、制造商的对应

 

(4) 有时我们为了调试调动而要不断地去更新驱动

此时USB是连接上的,此时你按第二步去更新驱动程序,会发现如下提示:

这是由于在更新驱动时系统会去比较inf文件中的DriverVer字段,如果发现它比之前的版本一样或低,那么就不进行加载了。

所以我们的方法是把DriverVer字段的值改大一点就可以了。

 

 

4、开发过程(源代码)

采用WDM结构开发的。

编译环境可参考我 Window XP驱动开发 系列专栏里讲到的文章.

 

我的所有代码集中在一个文件ezusbsys.c中,现在把代码与大家共享。

由于此文件太长,CSDN上显示不了,我决定把它分为四部分显示,大家把它拼起来即可。

这样我们就得到了  ezusbdrv.sys 文件

 

(1)我们来看设备创建时IRP_MJ_CREATE对应的回调函数;

DriverObject->MajorFunction[IRP_MJ_CREATE] = Ezusb_Create;

它是当调用CreateFile时调用的。

Ezusb_Create做的事情很简单,主要是创建Link,然后结束IRP的请求(使用IoCompleteRequest),并让派遣函数返回成功

(可参考我的文章: <<Window XP驱动开发(二十一)Window驱动的派遣函数>>)

(2)我们来看DriverObject->MajorFunction[IRP_MJ_PNP] = Ezusb_DispatchPnp;

它主要负责计算机中即插即用的处理:

(2、1)从栈中从Irp中获得信息; irpStack = IoGetCurrentIrpStackLocation (Irp);

(2、2)获得操作码;  fcn = irpStack->MinorFunction;
(2、3)根据操作码对应相应的动作,重点。

动作有:

(2、3、1)IRP_MN_START_DEVICE

对应的回调函数是

NTSTATUS
Ezusb_HandleStartDevice(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )

它主要做了以下三件事:

转发IRP_MN_START_DEVICE请求到底层;IoCopyCurrentIrpStackLocationToNext(Irp);

设置完成例程; IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,(PVOID) &event, TRUE, TRUE, TRUE);
等待完成;      KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

(2、3、2)RP_MN_STOP_DEVICE

首先把请求向下传给栈,再调用Ezusb_StopDevice;

 Ezusb_DefaultPnpHandler(fdo,Irp);

         ntStatus = Ezusb_StopDevice(fdo);


Ezusb_StopDevice主要完成的事情用一个空指针发送URB的配置,这样就关闭了配置并把设备放到了“未配置”状态。

(2、3、3)IRP_MN_REMOVE_DEVICE

NTSTATUS
Ezusb_HandleRemoveDevice(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )


 

(2、3、4)IRP_MN_QUERY_CAPABILITIES;

 

 

(3)

 第一部分:

 

 

 

#include <wdm.h>
#include "stdarg.h"
#include "stdio.h"

//
// Include files needed for USB support
//
#include "usbdi.h"
#include "usbdlib.h"

//
// Include file for the Ezusb Device
//
#include "ezusbsys.h"

//
// incude file containing driver version
//
#include "version.h"

#ifdef DOWNLOAD_KEIL_MONITOR

extern INTEL_HEX_RECORD mon_ext_sio1_ezusb[];
extern INTEL_HEX_RECORD mon_ext_sio1_fx2[];
extern INTEL_HEX_RECORD loader[];

#endif // ifdef DOWNLOAD_KEIL_MONITOR


void
DumpBuffer(PVOID pvBuffer, ULONG length);

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:
   Installable driver initialization entry point.
   This is where the driver is called when the driver is being loaded
   by the I/O system.  This entry point is called directly by the I/O system.

Arguments:
   DriverObject - pointer to the driver object
   RegistryPath - pointer to a unicode string representing the path
                  to driver-specific key in the registry

Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS ntStatus = STATUS_SUCCESS;
   PDEVICE_OBJECT deviceObject = NULL;

   Ezusb_KdPrint (("entering (Ezusb) DriverEntry (Build: %s/%s\n",__DATE__,__TIME__));

   //
   // Create dispatch points for the various events handled by this
   // driver.  For example, device I/O control calls (e.g., when a Win32
   // application calls the DeviceIoControl function) will be dispatched to
   // routine specified below in the IRP_MJ_DEVICE_CONTROL case.
   //
   DriverObject->MajorFunction[IRP_MJ_CREATE] = Ezusb_Create;
   DriverObject->MajorFunction[IRP_MJ_CLOSE] = Ezusb_Close;
   DriverObject->DriverUnload = Ezusb_Unload;

   DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Ezusb_ProcessIOCTL;

   DriverObject->MajorFunction[IRP_MJ_PNP] = Ezusb_DispatchPnp;
   DriverObject->MajorFunction[IRP_MJ_POWER] = Ezusb_DispatchPower;
   DriverObject->DriverExtension->AddDevice = Ezusb_PnPAddDevice;

   Ezusb_KdPrint (("exiting (Ezusb) DriverEntry (%x)\n", ntStatus));

   return ntStatus;
}

NTSTATUS
Ezusb_DefaultPnpHandler(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

   IoSkipCurrentIrpStackLocation(Irp);
   return IoCallDriver(pdx->StackDeviceObject, Irp);
}

///
// @func Handle completion of a request by a lower-level driver
// @parm Functional device object
// @parm I/O request which has completed
// @parm Context argument supplied to IoSetCompletionRoutine, namely address of
// KEVENT object on which ForwardAndWait is waiting
// @comm This is the completion routine used for requests forwarded by ForwardAndWait. It
// sets the event object and thereby awakens ForwardAndWait.
// @comm Note that it's *not* necessary for this particular completion routine to test
// the PendingReturned flag in the IRP and then call IoMarkIrpPending. You do that in many
// completion routines because the dispatch routine can't know soon enough that the
// lower layer has returned STATUS_PENDING. In our case, we're never going to pass a
// STATUS_PENDING back up the driver chain, so we don't need to worry about this.

NTSTATUS 
OnRequestComplete(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp,
   IN PKEVENT pev
   )
/*++

Routine Description:
   Handle completion of a request by a lower-level driver

Arguments:
   DriverObject -  Functional device object
   Irp - I/O request which has completed
   pev - Context argument supplied to IoSetCompletionRoutine, namely address of
         KEVENT object on which ForwardAndWait is waiting

Return Value:
   STATUS_MORE_PROCESSING_REQUIRED
--*/
{
   KeSetEvent(pev, 0, FALSE);
   return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS
ForwardAndWait(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
/*++
Routine Description:
   Forward request to lower level and await completion

   The only purpose of this routine in this particular driver is to pass down
   IRP_MN_START_DEVICE requests and wait for the PDO to handle them.
   
   The processor must be at PASSIVE IRQL because this function initializes
   and waits for non-zero time on a kernel event object.

Arguments:
   fdo - pointer to a device object
   Irp          - pointer to an I/O Request Packet

Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise
--*/
{
	KEVENT event;
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
	NTSTATUS ntStatus;

   ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
	
	//
   // Initialize a kernel event object to use in waiting for the lower-level
	// driver to finish processing the object. 
   //
	KeInitializeEvent(&event, NotificationEvent, FALSE);

	IoCopyCurrentIrpStackLocationToNext(Irp);
	IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete,
		(PVOID) &event, TRUE, TRUE, TRUE);

	ntStatus = IoCallDriver(pdx->StackDeviceObject, Irp);

	if (ntStatus == STATUS_PENDING)
	{
      KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
      ntStatus = Irp->IoStatus.Status;
   }

	return ntStatus;
}

NTSTATUS
CompleteRequest(
   IN PIRP Irp,
   IN NTSTATUS status,
   IN ULONG info
   )
/*++
Routine Description:
   Mark I/O request complete

Arguments:
   Irp - I/O request in question
   status - Standard status code
   info Additional information related to status code

Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise
--*/
{
	Irp->IoStatus.Status = status;
	Irp->IoStatus.Information = info;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);

   return status;
}

NTSTATUS
Ezusb_DispatchPnp(
   IN PDEVICE_OBJECT fdo,
   IN PIRP           Irp
   )
/*++
Routine Description:
   Process Plug and Play IRPs sent to this device.

Arguments:
   fdo - pointer to a device object
   Irp          - pointer to an I/O Request Packet

Return Value:
   NTSTATUS
--*/
{
   PIO_STACK_LOCATION irpStack;
   PDEVICE_EXTENSION pdx = fdo->DeviceExtension;
   ULONG fcn;
   NTSTATUS ntStatus;

   Ezusb_KdPrint (("Enter Ezusb_DispatchPnp\n"));

   if (!LockDevice(fdo))
		return CompleteRequest(Irp, STATUS_DELETE_PENDING, 0);

   //
   // Get a pointer to the current location in the Irp. This is where
   //     the function codes and parameters are located.
   //
   irpStack = IoGetCurrentIrpStackLocation (Irp);

   ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);

   fcn = irpStack->MinorFunction;

   switch (fcn)
   {
      case IRP_MN_START_DEVICE:

         Ezusb_KdPrint (("IRP_MN_START_DEVICE\n"));

         ntStatus = Ezusb_HandleStartDevice(fdo,Irp);

         if (ntStatus == STATUS_SUCCESS)
         {
            pdx->Started = TRUE;
         }

         break; //IRP_MN_START_DEVICE

      case IRP_MN_STOP_DEVICE:

         Ezusb_KdPrint (("IRP_MN_STOP_DEVICE\n"));

         //
         // first pass the request down the stack
         //
         Ezusb_DefaultPnpHandler(fdo,Irp);

         ntStatus = Ezusb_StopDevice(fdo);

         break; //IRP_MN_STOP_DEVICE

      case IRP_MN_REMOVE_DEVICE:

         Ezusb_KdPrint (("IRP_MN_REMOVE_DEVICE\n"))

         ntStatus = Ezusb_HandleRemoveDevice(fdo,Irp);

         break; //IRP_MN_REMOVE_DEVICE

      case IRP_MN_QUERY_CAPABILITIES:
      {
         //
         // This code swiped from Walter Oney.  Please buy his book!!
         //

      	PDEVICE_CAPABILITIES pdc = irpStack->Parameters.DeviceCapabilities.Capabilities;

         Ezusb_KdPrint (("IRP_MN_QUERY_CAPABILITIES\n"))

         // Check to be sure we know how to handle this version of the capabilities structure

	      if (pdc->Version < 1)
         {
		      ntStatus = Ezusb_DefaultPnpHandler(fdo, Irp);
            break;
         }

         ntStatus = ForwardAndWait(fdo, Irp);
	      if (NT_SUCCESS(ntStatus))
   		{						// IRP succeeded
      		pdc = irpStack->Parameters.DeviceCapabilities.Capabilities;
            // setting this field prevents NT5 from notifying the user when the
            // device is removed.
		      pdc->SurpriseRemovalOK = TRUE;
   		}						// IRP succeeded

	      ntStatus = CompleteRequest(Irp, ntStatus, Irp->IoStatus.Information);
      }
         break; //IRP_MN_QUERY_CAPABILITIES


      //
      // All other PNP IRP's are just passed down the stack by the default handler
      //
      default:
        Ezusb_KdPrint (("Passing down unhandled PnP IOCTL 0x%x\n", fcn));
        ntStatus = Ezusb_DefaultPnpHandler(fdo, Irp);

   } // switch MinorFunction

	if (fcn != IRP_MN_REMOVE_DEVICE)
      UnlockDevice(fdo);

   Ezusb_KdPrint (("Exit Ezusb_DispatchPnp %x\n", ntStatus));
   return ntStatus;

}//Ezusb_Dispatch


NTSTATUS
Ezusb_DispatchPower(
    IN PDEVICE_OBJECT fdo,
    IN PIRP           Irp
    )
/*++
Routine Description:
   Process the IRPs sent to this device.

Arguments:
   fdo - pointer to a device object
   Irp          - pointer to an I/O Request Packet

Return Value:
   NTSTATUS
--*/
{
   PIO_STACK_LOCATION irpStack, nextStack;
   PDEVICE_EXTENSION pdx = fdo->DeviceExtension;
   NTSTATUS ntStatus;

   Ezusb_KdPrint (("Enter Ezusb_DispatchPower\n"));

   Irp->IoStatus.Status = STATUS_SUCCESS;
   Irp->IoStatus.Information = 0;

   //
   // Get a pointer to the current location in the Irp. This is where
   //     the function codes and parameters are located.
   //
   irpStack = IoGetCurrentIrpStackLocation (Irp);



   Ezusb_KdPrint (("IRP_MJ_POWER MIN=0x%x Type=0x%x State=0x%x\n",irpStack->MinorFunction,
      irpStack->Parameters.Power.Type,
      irpStack->Parameters.Power.State.DeviceState));

   switch (irpStack->MinorFunction)
   {
      case IRP_MN_SET_POWER:

         switch (irpStack->Parameters.Power.Type)
         {
            case SystemPowerState:

               break; //SystemPowerState

            case DevicePowerState:
               switch (irpStack->Parameters.Power.State.DeviceState)
               {
                  case PowerDeviceD3:
                     Ezusb_KdPrint (("IRP_MN_SET_D3\n"));
                     break;
                  case PowerDeviceD2:
                     Ezusb_KdPrint (("IRP_MN_SET_D2\n"));
                     break;
                  case PowerDeviceD1:
                     Ezusb_KdPrint (("IRP_MN_SET_D1\n"));
                     break;
                  case PowerDeviceD0:
                     Ezusb_KdPrint (("IRP_MN_SET_D0\n"));
                     break;
                } // switch on Power.State.DeviceState

                break; //DevicePowerState

         }// switch on Power.Type

         break;  //IRP_MN_SET_POWER

      case IRP_MN_QUERY_POWER:

         // Look at what type of power query this is
         switch (irpStack->Parameters.Power.Type)
         {
            case SystemPowerState:

               break; //SystemPowerState

            case DevicePowerState:
               switch (irpStack->Parameters.Power.State.DeviceState)
               {
                  case PowerDeviceD2:
                     Ezusb_KdPrint (("IRP_MN_QUERY_D2\n"));
                     break;
                  case PowerDeviceD1:
                     Ezusb_KdPrint (("IRP_MN_QUERY_D1\n"));
                     break;
                  case PowerDeviceD3:
                     Ezusb_KdPrint (("IRP_MN_QUERY_D3\n"));
                     break;
               } //switch on Power.State.DeviceState

               break; //DevicePowerState
                            
         }//switch on Power.Type

         break; //IRP_MN_QUERY_POWER

      default:
         // A PnP Minor Function was not handled
         Ezusb_KdPrint (("Power IOCTL not handled\n"));

   } /* switch MinorFunction*/


   nextStack = IoGetNextIrpStackLocation(Irp);
   ASSERT(nextStack != NULL);
   RtlCopyMemory(nextStack, irpStack, sizeof(IO_STACK_LOCATION));

   //
   // All PNP_POWER messages get passed to the StackDeviceObject that
   // we were given in PnPAddDevice.
   //
   // This stack device object is managed by the USB software subsystem,
   // and so this IRP must be propagated to the owning device driver for
   // that stack device object, so that driver in turn can perform any
   // device state management (e.g., remove its device object, etc.).
   //
   Ezusb_KdPrint (("Passing Power Irp down\n"));

   //
   // Notes on passing power IRPs down:  Using IoCallDriver() to pass
   // down power IRPs worked until Windows 2000.  Using this method
   // with Win2K causes a blue screen at system shutdown.  Because of this,
   // I am modifying the driver to use the more correct PoXXX() functions
   // to handle power IRPs. Unfortunately, the PoXXX() calls weren't
   // added until the kernel until after the release of Windows 95 OSR2.
   // So, a driver using these calls will not load on a Windows 95 system.
   // If you need to use this driver under Windows 95. then you must
   // #define WIN95.
   //

#ifdef WIN95
   ntStatus = IoCallDriver(pdx->StackDeviceObject, Irp);
#else
   PoStartNextPowerIrp(Irp);
   ntStatus = PoCallDriver(pdx->StackDeviceObject,Irp);
#endif

   //
   // If lower layer driver marked the Irp as pending then reflect that by
   // calling IoMarkIrpPending.
   //
   if (ntStatus == STATUS_PENDING)
   {
      IoMarkIrpPending(Irp);
      Ezusb_KdPrint (("Power Irp came back with STATUS_PENDING (%x)\n", ntStatus));
   }
   else
   {
      Ezusb_KdPrint (("Power Irp came back, status = %x\n", ntStatus));
   } // if ntStatus is PENDING

   goto Ezusb_Dispatch_Done;
            
Ezusb_Dispatch_Done:

   Ezusb_KdPrint (("Exit Ezusb_DispatchPower %x\n", ntStatus));
   return ntStatus;

}//Ezusb_DispatchPower


VOID
Ezusb_Unload(
    IN PDRIVER_OBJECT DriverObject
    )
/*++
Routine Description:
    Free all the allocated resources, etc.
    TODO: This is a placeholder for driver writer to add code on unload

Arguments:
    DriverObject - pointer to a driver object

Return Value:
    None
--*/
{
    Ezusb_KdPrint (("enter Ezusb_Unload\n"));
    /*
    // TODO: Free any global resources allocated in DriverEntry
    */
    Ezusb_KdPrint (("exit Ezusb_Unload\n"));
}

NTSTATUS
Ezusb_HandleRemoveDevice(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   NTSTATUS ntStatus;
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
   ULONG i;

   // set the removing flag to prevent any new I/O's
   pdx->removing = TRUE;

   // brute force - send an abort pipe message to all pipes to cancel any
   // pending transfers.  this should solve the problem of the driver blocking
   // on a REMOVE message because there is a pending transfer.
   for (i = 0; i < pdx->Interface->NumberOfPipes; i++)
   {
      Ezusb_AbortPipe(fdo,(USBD_PIPE_HANDLE) pdx->Interface->Pipes[i].PipeHandle);
   }

   UnlockDevice(fdo);			// once for LockDevice at start of dispatch
   UnlockDevice(fdo);			// once for initialization during AddDevice
   KeWaitForSingleObject(&pdx->evRemove, Executive, KernelMode, FALSE, NULL);

	Ezusb_RemoveDevice(fdo);

   ntStatus = Ezusb_DefaultPnpHandler(fdo, Irp);

   return ntStatus;				// lower-level completed IoStatus already

}


NTSTATUS
Ezusb_HandleStartDevice(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   NTSTATUS ntStatus;

   //
   // First let all lower-level drivers handle this request.
   //
   ntStatus = ForwardAndWait(fdo, Irp);
	if (!NT_SUCCESS(ntStatus))
		return CompleteRequest(Irp, ntStatus, Irp->IoStatus.Information);

   //
   // now do whatever we need to do to start the device
   //
   ntStatus = Ezusb_StartDevice(fdo);

	return CompleteRequest(Irp, ntStatus, 0);
}

NTSTATUS
Ezusb_StartDevice(
    IN  PDEVICE_OBJECT fdo
    )
/*++

Routine Description:
   Initializes a given instance of the Ezusb Device on the USB.

   Arguments:
      fdo - pointer to the device object for this instance of a
                      Ezusb Device

Return Value:
   NT status code
--*/
{
    PDEVICE_EXTENSION pdx;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
    PURB urb;
    ULONG siz;

    Ezusb_KdPrint (("enter Ezusb_StartDevice\n"));

    pdx = fdo->DeviceExtension;
    pdx->NeedCleanup = TRUE;

    /*
    // Get some memory from then non paged pool (fixed, locked system memory)
    // for use by the USB Request Block (urb) for the specific USB Request we
    // will be performing below (a USB device request).
    */
    urb = ExAllocatePool( NonPagedPool,
                          sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if (urb) {

        siz = sizeof(USB_DEVICE_DESCRIPTOR);

        // Get some non paged memory for the device descriptor contents
        deviceDescriptor = ExAllocatePool(NonPagedPool,
                                          siz);

        if (deviceDescriptor) {

            // Use a macro in the standard USB header files to build the URB
            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_DEVICE_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         deviceDescriptor,
                                         NULL,
                                         siz,
                                         NULL);

            // Get the device descriptor
            ntStatus = Ezusb_CallUSBD(fdo, urb);

            // Dump out the descriptor info to the debugger
            if (NT_SUCCESS(ntStatus)) {
                Ezusb_KdPrint (("Device Descriptor = %x, len %x\n",
                                deviceDescriptor,
                                urb->UrbControlDescriptorRequest.TransferBufferLength));

                Ezusb_KdPrint (("Ezusb Device Descriptor:\n"));
                Ezusb_KdPrint (("-------------------------\n"));
                Ezusb_KdPrint (("bLength %d\n", deviceDescriptor->bLength));
                Ezusb_KdPrint (("bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType));
                Ezusb_KdPrint (("bcdUSB 0x%x\n", deviceDescriptor->bcdUSB));
                Ezusb_KdPrint (("bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass));
                Ezusb_KdPrint (("bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass));
                Ezusb_KdPrint (("bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol));
                Ezusb_KdPrint (("bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0));
                Ezusb_KdPrint (("idVendor 0x%x\n", deviceDescriptor->idVendor));
                Ezusb_KdPrint (("idProduct 0x%x\n", deviceDescriptor->idProduct));
                Ezusb_KdPrint (("bcdDevice 0x%x\n", deviceDescriptor->bcdDevice));
                Ezusb_KdPrint (("iManufacturer 0x%x\n", deviceDescriptor->iManufacturer));
                Ezusb_KdPrint (("iProduct 0x%x\n", deviceDescriptor->iProduct));
                Ezusb_KdPrint (("iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber));
                Ezusb_KdPrint (("bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations));
            }
        } else {
            ntStatus = STATUS_NO_MEMORY;
        }

        if (NT_SUCCESS(ntStatus)) {
            /*
            // Put a ptr to the device descriptor in the device extension for easy
            // access (like a "cached" copy).  We will free this memory when the
            // device is removed.  See the "Ezusb_RemoveDevice" code.
            */
            pdx->DeviceDescriptor = deviceDescriptor;
            pdx->Stopped = FALSE;
        } else if (deviceDescriptor) {
            /*
            // If the bus transaction failed, then free up the memory created to hold
            // the device descriptor, since the device is probably non-functional
            */
            ExFreePool(deviceDescriptor);
            pdx->DeviceDescriptor = NULL;
        }

        ExFreePool(urb);

    } else {
        // Failed getting memory for the Urb 
        ntStatus = STATUS_NO_MEMORY;
    }

    // If the Get_Descriptor call was successful, then configure the device.
    if (NT_SUCCESS(ntStatus)) {
        ntStatus = Ezusb_ConfigureDevice(fdo);
    }

#ifdef DOWNLOAD_KEIL_MONITOR
   //
   // download the Keil monitor
   //

   //
   // First download loader firmware.  The loader firmware implements a vendor
   // specific command that will allow us to anchor load to external ram
   //
   Ezusb_8051Reset(fdo,1);
   Ezusb_DownloadIntelHex(fdo,loader);
   Ezusb_8051Reset(fdo,0);

   //
   // Now download the Keil Monitor
   //
   if (IsFx2(fdo))
   {
      Ezusb_KdPrint (("**** Downloading FX2 monitor\n"));
      Ezusb_DownloadIntelHex(fdo,mon_ext_sio1_fx2);
   }
   else
   {
      Ezusb_KdPrint (("**** Downloading EZ-USB monitor\n"));
      Ezusb_DownloadIntelHex(fdo,mon_ext_sio1_ezusb);
   }

   Ezusb_8051Reset(fdo,1);
   Ezusb_8051Reset(fdo,0);

#endif // if DOWNLOAD_KEIL_MONITOR

    Ezusb_KdPrint (("exit Ezusb_StartDevice (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
Ezusb_RemoveDevice(
    IN  PDEVICE_OBJECT fdo
    )
/*++

Routine Description:
    Removes a given instance of a Ezusb Device device on the USB.

Arguments:
    fdo - pointer to the device object for this instance of a Ezusb Device

Return Value:
    NT status code

--*/
{
   PDEVICE_EXTENSION pdx;
   NTSTATUS ntStatus = STATUS_SUCCESS;

   Ezusb_KdPrint (("enter Ezusb_RemoveDevice\n"));

   pdx = fdo->DeviceExtension;

   if (pdx->DeviceDescriptor)
   {
      ExFreePool(pdx->DeviceDescriptor);
   }

   //
   // Free up any interface structures in our device extension
   //
   if (pdx->Interface != NULL)
   {
      ExFreePool(pdx->Interface);
   }

   //
   // remove the device object's symbolic link
   //
   if (pdx->NeedCleanup)
   {
      UNICODE_STRING deviceLinkUnicodeString;

      pdx->NeedCleanup = FALSE;

      RtlInitUnicodeString (&deviceLinkUnicodeString,
                           pdx->DeviceLinkNameBuffer);

      IoDeleteSymbolicLink(&deviceLinkUnicodeString);
    }

   IoDetachDevice(pdx->StackDeviceObject);

   IoDeleteDevice (fdo);

   Ezusb_KdPrint (("exit Ezusb_RemoveDevice (%x)\n", ntStatus));

   return ntStatus;
}


NTSTATUS
Ezusb_StopDevice(
   IN  PDEVICE_OBJECT fdo
   )
/*++
Routine Description:
   Stops a given instance of a Ezusb Device device on USB.

Arguments:
   fdo - pointer to the device object for this instance of a Ezusb Device

Return Value:
   NT status code

  --*/
{
   PDEVICE_EXTENSION pdx;
   NTSTATUS ntStatus = STATUS_SUCCESS;
   PURB urb;
   ULONG siz;

   Ezusb_KdPrint (("enter Ezusb_StopDevice\n"));

   pdx = fdo->DeviceExtension;

   //
   // Send the select configuration urb with a NULL pointer for the configuration
   // handle, this closes the configuration and puts the device in the 'unconfigured'
   // state.
   //

   siz = sizeof(struct _URB_SELECT_CONFIGURATION);

   urb = ExAllocatePool(NonPagedPool,
                      siz);

   if (urb)
   {
      NTSTATUS status;

      UsbBuildSelectConfigurationRequest(urb,
                                       (USHORT) siz,
                                       NULL);

      status = Ezusb_CallUSBD(fdo, urb);

      Ezusb_KdPrint (("Device Configuration Closed status = %x usb status = %x.\n",
                     status, urb->UrbHeader.Status));

      ExFreePool(urb);
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;
   }

   Ezusb_KdPrint (("exit Ezusb_StopDevice (%x)\n", ntStatus));

   return ntStatus;
}

NTSTATUS
Ezusb_PnPAddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject
    )
/*++
Routine Description:
    This routine is called to create a new instance of the device

Arguments:
    DriverObject - pointer to the driver object for this instance of Ezusb
    PhysicalDeviceObject - pointer to a device object created by the bus

Return Value:
    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS                ntStatus = STATUS_SUCCESS;
   PDEVICE_OBJECT          deviceObject = NULL;
   PDEVICE_EXTENSION       pdx;
   int instance;

   Ezusb_KdPrint(("enter Ezusb_PnPAddDevice\n"));

#define MAX_EZUSB_DEVICES 8

   //
   // create our functional device object (FDO).  This driver supports multiple ezusb devices.
   // This loop will look for an available instance number.  Keep incrementing the instance
   // until a call to Ezusb_CreateDeviceObject succeeds.
   //
   instance = 0;
   do
   {
      ntStatus = Ezusb_CreateDeviceObject(DriverObject, &deviceObject, instance);
      instance++;
   } while (!NT_SUCCESS(ntStatus) && (instance < MAX_EZUSB_DEVICES));

   if (NT_SUCCESS(ntStatus))
   {
      pdx = deviceObject->DeviceExtension;

      //
      // Non plug and play drivers usually create the device object in
      // driver entry, and the I/O manager autimatically clears this flag.
      // Since we are creating the device object ourselves in response to 
      // a PnP START_DEVICE IRP, we need to clear this flag ourselves.
      //
      deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

      //
      // This driver uses direct I/O for read/write requests
      //
      deviceObject->Flags |= DO_DIRECT_IO;

      deviceObject->Flags |= DO_POWER_PAGABLE;

      //
      //
      // store away the Physical device Object
      //
      pdx->PhysicalDeviceObject=PhysicalDeviceObject;

      //
      // Attach to the StackDeviceObject.  This is the device object that what we 
      // use to send Irps and Urbs down the USB software stack
      //
      pdx->StackDeviceObject =
         IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);

      ASSERT (pdx->StackDeviceObject != NULL);

      pdx->LastFailedUrbStatus = 0;

	   pdx->usage = 1;				// locked until RemoveDevice
	   KeInitializeEvent(&pdx->evRemove,
                        NotificationEvent,
                        FALSE);              // set when use count drops to zero
   }

   Ezusb_KdPrint(("exit Ezusb_PnPAddDevice (%x)\n", ntStatus));

   return ntStatus;
}


第二部分:

NTSTATUS
Ezusb_CreateDeviceObject(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT *DeviceObject,
    LONG Instance
    )
/*++

Routine Description:
    Creates a Functional DeviceObject

Arguments:
    DriverObject - pointer to the driver object for device
    DeviceObject - pointer to DeviceObject pointer to return
                   created device object.
    Instance - instnace of the device create.

Return Value:
    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise
--*/
{
   NTSTATUS ntStatus;
   WCHAR deviceLinkBuffer[]  = L"\\DosDevices\\Ezusb-0";
   UNICODE_STRING deviceLinkUnicodeString;
   WCHAR deviceNameBuffer[]  = L"\\Device\\Ezusb-0";
   UNICODE_STRING deviceNameUnicodeString;
   PDEVICE_EXTENSION pdx;
   STRING deviceName;

   Ezusb_KdPrint(("enter Ezusb_CreateDeviceObject instance = %d\n", Instance));

   //
   // fix up device names based on Instance
   //
   deviceLinkBuffer[18] = (USHORT) ('0' + Instance);
   deviceNameBuffer[14] = (USHORT) ('0' + Instance);

   Ezusb_KdPrint(("Create Device name (%ws)\n", deviceNameBuffer));

   RtlInitUnicodeString (&deviceNameUnicodeString,
                         deviceNameBuffer);

   //
   //Print out the unicode string
   //NOTE:  We must first convert the string to Unicode due to a bug in the Debugger that does not allow
   //       Unicode Strings to be printed to the debug device.
   //
   deviceName.Buffer = NULL;

   ntStatus = RtlUnicodeStringToAnsiString (&deviceName,
                                          &deviceNameUnicodeString, 
                                          TRUE);


   if (NT_SUCCESS(ntStatus))
   {
      Ezusb_KdPrint(("Create Device Name (%s)\n", deviceName.Buffer));
      RtlFreeAnsiString (&deviceName);
   }
   else
   {
      Ezusb_KdPrint(("Unicode to Ansi str failed w/ ntStatus: 0x%x\n",ntStatus));
   }

   ntStatus = IoCreateDevice (DriverObject,
                              sizeof (DEVICE_EXTENSION),
                              &deviceNameUnicodeString,
                              FILE_DEVICE_UNKNOWN,
                              0,
                              FALSE,
                              DeviceObject);


   if (NT_SUCCESS(ntStatus))
   {

      // Initialize our device extension
      pdx = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);

      RtlCopyMemory(pdx->DeviceLinkNameBuffer,
                   deviceLinkBuffer,
                   sizeof(deviceLinkBuffer));

      pdx->OpenHandles = 0;
      pdx->ConfigurationHandle = NULL;
      pdx->DeviceDescriptor = NULL;
      pdx->NeedCleanup = FALSE;
      pdx->DataRingBuffer = NULL;
      pdx->DescriptorRingBuffer = NULL;
      pdx->Started = FALSE;

      // Initialize our interface
      pdx->Interface = NULL;

      RtlInitUnicodeString (&deviceLinkUnicodeString,
                           deviceLinkBuffer);

      Ezusb_KdPrint(("Create DosDevice name (%ws)\n", deviceLinkBuffer));

      ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
                                      &deviceNameUnicodeString);

   }

   Ezusb_KdPrint(("exit Ezusb_CreateDeviceObject (%x)\n", ntStatus));

   return ntStatus;
}

NTSTATUS
Ezusb_CallUSBD(
    IN PDEVICE_OBJECT fdo,
    IN PURB Urb
    )
/*++

Routine Description:
   Passes a Usb Request Block (URB) to the USB class driver (USBD)

   Note that we create our own IRP here and use it to send the request to
   the USB software subsystem.  This means that this routine is essentially
   independent of the IRP that caused this driver to be called in the first
   place.  The IRP for this transfer is created, used, and then destroyed
   in this routine.

   However, note that this routine uses the Usb Request Block (urb) passed
   in by the caller as the request block for the USB software stack.

   Implementation of this routine may be changed depending on the specific
   requirements of your driver.  For example, while this routine issues a
   synchronous request to the USB stack, you may wish to implement this as an
   asynchronous request in which you set an IoCompletionRoutine to be called
   when the request is complete.

Arguments:
   fdo - pointer to the device object for this instance of an Ezusb Device
   Urb          - pointer to Urb request block

Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS ntStatus, status = STATUS_SUCCESS;
   PDEVICE_EXTENSION pdx;
   PIRP irp;
   KEVENT event;
   IO_STATUS_BLOCK ioStatus;
   PIO_STACK_LOCATION nextStack;

   Ezusb_KdPrint (("enter Ezusb_CallUSBD\n"));

   pdx = fdo->DeviceExtension;

   // issue a synchronous request (see notes above)
   KeInitializeEvent(&event, NotificationEvent, FALSE);

   irp = IoBuildDeviceIoControlRequest(
             IOCTL_INTERNAL_USB_SUBMIT_URB,
             pdx->StackDeviceObject,
             NULL,
             0,
             NULL,
             0,
             TRUE, /* INTERNAL */
             &event,
             &ioStatus);

   // Prepare for calling the USB driver stack
   nextStack = IoGetNextIrpStackLocation(irp);
   ASSERT(nextStack != NULL);

   // Set up the URB ptr to pass to the USB driver stack
   nextStack->Parameters.Others.Argument1 = Urb;

   Ezusb_KdPrint (("Calling USB Driver Stack\n"));

   //
   // Call the USB class driver to perform the operation.  If the returned status
   // is PENDING, wait for the request to complete.
   //
   ntStatus = IoCallDriver(pdx->StackDeviceObject,
                         irp);

   Ezusb_KdPrint (("return from IoCallDriver USBD %x\n", ntStatus));

   if (ntStatus == STATUS_PENDING)
   {
      Ezusb_KdPrint (("Wait for single object\n"));

      status = KeWaitForSingleObject(
                    &event,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);

      Ezusb_KdPrint (("Wait for single object, returned %x\n", status));
   }
   else
   {
      ioStatus.Status = ntStatus;
   }

   Ezusb_KdPrint (("URB status = %x status = %x irp status %x\n",
     Urb->UrbHeader.Status, status, ioStatus.Status));

   //
   // USBD maps the error code for us.  USBD uses error codes in its URB
   // structure that are more insightful into USB behavior. In order to
   // match the NT Status codes, USBD maps its error codes into more general NT
   // error categories so higher level drivers can decipher the error codes
   // based on standard NT error code definitions.
   //
   ntStatus = ioStatus.Status;

   //
   // If the URB status was not USBD_STATUS_SUCCESS, we save a copy of the
   // URB status in the device extension.  After a failure, another IOCTL,
   // IOCTL_EZUSB_GET_LAST_ERROR can be used to retrieve the URB status
   // for the most recently failed URB.  Of course, this status gets
   // overwritten by subsequent failures, but it's better than nothing.
   //
   if (!(USBD_SUCCESS(Urb->UrbHeader.Status)))
      pdx->LastFailedUrbStatus = Urb->UrbHeader.Status;

   //
   // if ioStatus.Status indicates an error (ie. the IRP failed) then return that.
   // If ioStatus.Status indicates success, it is still possible that what we were
   // trying to do failed.  For example, if the IRP is cancelled, the status returned
   // by the I/O manager for the IRP will not indicate an error.  In that case, we
   // should check the URB status.  If it indicates anything other than
   // USBD_SUCCESS, then we should return STATUS_UNSUCCESSFUL.
   //
   if (NT_SUCCESS(ntStatus))
   {
      if (!(USBD_SUCCESS(Urb->UrbHeader.Status)))
         ntStatus = STATUS_UNSUCCESSFUL;
   }

   Ezusb_KdPrint(("exit Ezusb_CallUSBD (%x)\n", ntStatus));

   return ntStatus;
}

NTSTATUS
Ezusb_ConfigureDevice(
    IN  PDEVICE_OBJECT fdo
    )
/*++
Routine Description:
   Configures the USB device via USB-specific device requests and interaction
   with the USB software subsystem.

Arguments:
   fdo - pointer to the device object for this instance of the Ezusb Device

Return Value:
   NT status code
--*/
{
   PDEVICE_EXTENSION pdx;
   NTSTATUS ntStatus;
   PURB urb = NULL;
   ULONG siz;
   PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;

   Ezusb_KdPrint (("enter Ezusb_ConfigureDevice\n"));

   pdx = fdo->DeviceExtension;

   //
   // Get memory for the USB Request Block (urb).
   //
   urb = ExAllocatePool(NonPagedPool,
                      sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

   if (urb != NULL)
   {
      //
      // Set size of the data buffer.  Note we add padding to cover hardware faults
      // that may cause the device to go past the end of the data buffer
      //
        siz = sizeof(USB_CONFIGURATION_DESCRIPTOR) + 16;

        // Get the nonpaged pool memory for the data buffer
        configurationDescriptor = ExAllocatePool(NonPagedPool, siz);

        if (configurationDescriptor != NULL) {

            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_CONFIGURATION_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         configurationDescriptor,
                                         NULL,
                                         sizeof (USB_CONFIGURATION_DESCRIPTOR),/* Get only the configuration descriptor */
                                         NULL);

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            if (NT_SUCCESS(ntStatus)) {
                Ezusb_KdPrint (("Configuration Descriptor is at %x, bytes txferred: %d\n\
                                  Configuration Descriptor Actual Length: %d\n",
                                  configurationDescriptor,
                                  urb->UrbControlDescriptorRequest.TransferBufferLength,
                                  configurationDescriptor->wTotalLength));
            }//if

        } else {
            ntStatus = STATUS_NO_MEMORY;
            goto Exit_EzusbConfigureDevice;
        }//if-else

        // Determine how much data is in the entire configuration descriptor
        // and add extra room to protect against accidental overrun
        siz = configurationDescriptor->wTotalLength + 16;

        //  Free up the data buffer memory just used
        ExFreePool(configurationDescriptor);
        configurationDescriptor = NULL;

        // Get nonpaged pool memory for the data buffer
        configurationDescriptor = ExAllocatePool(NonPagedPool, siz);

        // Now get the entire Configuration Descriptor
        if (configurationDescriptor != NULL) {
            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_CONFIGURATION_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         configurationDescriptor,
                                         NULL,
                                         siz,  // Get all the descriptor data
                                         NULL);

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            if (NT_SUCCESS(ntStatus)) {
                Ezusb_KdPrint (("Entire Configuration Descriptor is at %x, bytes txferred: %d\n",
                                  configurationDescriptor,
                                  urb->UrbControlDescriptorRequest.TransferBufferLength));
            } else {
                //Error in getting configuration descriptor
                goto Exit_EzusbConfigureDevice;
            }//else

        } else {
            // Failed getting data buffer (configurationDescriptor) memory
            ntStatus = STATUS_NO_MEMORY;
            goto Exit_EzusbConfigureDevice;
        }//if-else

    } else {
        // failed getting urb memory
        ntStatus = STATUS_NO_MEMORY;
        goto Exit_EzusbConfigureDevice;
    }//if-else

    /*
    // We have the configuration descriptor for the configuration
    // we want.
    //
    // Now we issue the SelectConfiguration command to get
    // the  pipes associated with this configuration.
    */
    if (configurationDescriptor) {
        // Get our pipes
        ntStatus = Ezusb_SelectInterfaces(fdo,
                                           configurationDescriptor,
                                           NULL // Device not yet configured
                                           );
    } //if

Exit_EzusbConfigureDevice:

    // Clean up and exit this routine
    if (urb != NULL) {
        ExFreePool(urb);                    // Free urb memory
    }//if

    if (configurationDescriptor != NULL) {
        ExFreePool(configurationDescriptor);// Free data buffer
    }//if

    Ezusb_KdPrint (("exit Ezusb_ConfigureDevice (%x)\n", ntStatus));
    return ntStatus;
}//Ezusb_ConfigureDevice


NTSTATUS
Ezusb_SelectInterfaces(
    IN PDEVICE_OBJECT fdo,
    IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
    IN PUSBD_INTERFACE_INFORMATION Interface
    )
/*++

Routine Description:
    Initializes an Ezusb Device with multiple interfaces

Arguments:
    fdo            - pointer to the device object for this instance of the Ezusb Device
    ConfigurationDescriptor - pointer to the USB configuration descriptor containing the interface and endpoint
                              descriptors.
    Interface               - pointer to a USBD Interface Information Object
                            - If this is NULL, then this driver must choose its interface based on driver-specific
                              criteria, and the driver must also CONFIGURE the device.
                            - If it is NOT NULL, then the driver has already been given an interface and
                              the device has already been configured by the parent of this device driver.

Return Value:
    NT status code
--*/
{
   PDEVICE_EXTENSION pdx;
   NTSTATUS ntStatus;
   PURB urb;
   ULONG j;
   UCHAR alternateSetting, MyInterfaceNumber;
   PUSBD_INTERFACE_INFORMATION interfaceObject;
   USBD_INTERFACE_LIST_ENTRY interfaceList[2];

   Ezusb_KdPrint (("enter Ezusb_SelectInterfaces\n"));

   pdx = fdo->DeviceExtension;
   MyInterfaceNumber = SAMPLE_INTERFACE_NBR;

	// Search the configuration descriptor for the first interface/alternate setting

   interfaceList[0].InterfaceDescriptor =
   USBD_ParseConfigurationDescriptorEx(ConfigurationDescriptor,
                                       ConfigurationDescriptor,
                                       -1,         // Interface - don't care
                                       -1,         // Alternate Setting - don't care
                                       -1,         // Class - don't care
                                       -1,         // SubClass - don't care
                                       -1);        // Protocol - don't care

   ASSERT(interfaceList[0].InterfaceDescriptor != NULL);

   interfaceList[1].InterfaceDescriptor = NULL;
   interfaceList[1].Interface = NULL;

   urb = USBD_CreateConfigurationRequestEx(ConfigurationDescriptor,&interfaceList[0]);

   if (!urb)
   {
      Ezusb_KdPrint ((" USBD_CreateConfigurationRequestEx failed\n"));            
   }

   DumpBuffer(urb, urb->UrbHeader.Length);

   interfaceObject = (PUSBD_INTERFACE_INFORMATION) (&(urb->UrbSelectConfiguration.Interface));

   /*
   // We set up a default max transfer size for the endpoints.  Your driver will
   // need to change this to reflect the capabilities of your device's endpoints.
   */
   for (j=0; j<interfaceList[0].InterfaceDescriptor->bNumEndpoints; j++)
      interfaceObject->Pipes[j].MaximumTransferSize = (64 * 1024) - 1;


   ntStatus = Ezusb_CallUSBD(fdo, urb);

   DumpBuffer(urb, urb->UrbHeader.Length);

   if (NT_SUCCESS(ntStatus) && USBD_SUCCESS(urb->UrbHeader.Status))
   {

      // Save the configuration handle for this device
      pdx->ConfigurationHandle =
         urb->UrbSelectConfiguration.ConfigurationHandle;

      pdx->Interface = ExAllocatePool(NonPagedPool,
                                                interfaceObject->Length);

      // save a copy of the interfaceObject information returned
      RtlCopyMemory(pdx->Interface, interfaceObject, interfaceObject->Length);

      // Dump the interfaceObject to the debugger
      Ezusb_KdPrint (("---------\n"));
      Ezusb_KdPrint (("NumberOfPipes 0x%x\n", pdx->Interface->NumberOfPipes));
      Ezusb_KdPrint (("Length 0x%x\n", pdx->Interface->Length));
      Ezusb_KdPrint (("Alt Setting 0x%x\n", pdx->Interface->AlternateSetting));
      Ezusb_KdPrint (("Interface Number 0x%x\n", pdx->Interface->InterfaceNumber));

      // Dump the pipe info
      for (j=0; j<interfaceObject->NumberOfPipes; j++)
      {
         PUSBD_PIPE_INFORMATION pipeInformation;

         pipeInformation = &pdx->Interface->Pipes[j];

         Ezusb_KdPrint (("---------\n"));
         Ezusb_KdPrint (("PipeType 0x%x\n", pipeInformation->PipeType));
         Ezusb_KdPrint (("EndpointAddress 0x%x\n", pipeInformation->EndpointAddress));
         Ezusb_KdPrint (("MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize));
         Ezusb_KdPrint (("Interval 0x%x\n", pipeInformation->Interval));
         Ezusb_KdPrint (("Handle 0x%x\n", pipeInformation->PipeHandle));
         Ezusb_KdPrint (("MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize));
      }

      Ezusb_KdPrint (("---------\n"));
   }


   Ezusb_KdPrint (("exit Ezusb_SelectInterfaces (%x)\n", ntStatus));

   return ntStatus;

}/* Ezusb_SelectInterfaces */


NTSTATUS
Ezusb_Read_Write(
   IN  PDEVICE_OBJECT fdo,
   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
    
--*/
{
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   NTSTATUS                   ntStatus;
   PIO_STACK_LOCATION         irpStack = IoGetCurrentIrpStackLocation (Irp);
   PBULK_TRANSFER_CONTROL     bulkControl =
                              (PBULK_TRANSFER_CONTROL)Irp->AssociatedIrp.SystemBuffer;
   ULONG                      bufferLength =
                              irpStack->Parameters.DeviceIoControl.OutputBufferLength;
   PURB                       urb = NULL;
   ULONG                      urbSize = 0;
   ULONG                      transferFlags = 0;
   PUSBD_INTERFACE_INFORMATION interfaceInfo = NULL;
   PUSBD_PIPE_INFORMATION     pipeInfo = NULL;
   USBD_PIPE_HANDLE           pipeHandle = NULL;


   Ezusb_KdPrint(("enter Ezusb_Read_Write()\n"));
   
   //
   // verify that the selected pipe is valid, and get a handle to it. If anything
   // is wrong, return an error
   //
   interfaceInfo = pdx->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;
   }

   pipeInfo = &(interfaceInfo->Pipes[bulkControl->pipeNum]);

   if (!((pipeInfo->PipeType == UsbdPipeTypeBulk) ||
         (pipeInfo->PipeType == UsbdPipeTypeInterrupt)))
   {
      Ezusb_KdPrint(("Ezusb_Read_Write() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeHandle = pipeInfo->PipeHandle;

   if (!pipeHandle)
   {
      Ezusb_KdPrint(("Ezusb_Read_Write() invalid pipe - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }

   if (bufferLength > pipeInfo->MaximumTransferSize)
   {
      Ezusb_KdPrint(("Ezusb_Read_Write() invalid transfer size - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   //
   // allocate and fill in the Usb request (URB)
   //
   urbSize = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);

   urb = ExAllocatePool(NonPagedPool,urbSize);

   if (!urb)
   {
      Ezusb_KdPrint(("Ezusb_Read_Write() unable to alloc URB - Exiting\n"));
      return STATUS_NO_MEMORY;
   }
   

   transferFlags = USBD_SHORT_TRANSFER_OK;

   //
   // get direction info from the endpoint address
   //
   if (USB_ENDPOINT_DIRECTION_IN(pipeInfo->EndpointAddress))
      transferFlags |= USBD_TRANSFER_DIRECTION_IN;

   UsbBuildInterruptOrBulkTransferRequest(urb,        //ptr to urb
                        (USHORT) urbSize,             //size of urb
							   pipeHandle,                   //usbd pipe handle
							   NULL,                         //TransferBuffer
							   Irp->MdlAddress,              //mdl
							   bufferLength,                 //bufferlength
							   transferFlags,                //flags
							   NULL);                        //link

   //
   // Call the USB Stack.
   //
	ntStatus = Ezusb_CallUSBD(fdo, urb);

   //
   // If the transfer was successful, report the length of the transfer to the
   // caller by setting IoStatus.Information
   //
   if (NT_SUCCESS(ntStatus))
   {
      Irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
      Ezusb_KdPrint(("Successfully transfered 0x%x bytes\n",Irp->IoStatus.Information));
   }

   //
   // free the URB
   //
   ExFreePool(urb);

   return ntStatus;
}


NTSTATUS
Ezusb_Create(
    IN PDEVICE_OBJECT fdo,
    IN PIRP Irp
    )
/*++
Routine Description:
     This is the Entry point for CreateFile calls from user mode apps (apps may open "\\.\Ezusb-x\yyzz"
     where yy is the interface number and zz is the endpoint address).

     Here is where you would add code to create symbolic links between endpoints
     (i.e., pipes in USB software terminology) and User Mode file names.  You are
     free to use any convention you wish to create these links, although the above
     convention offers a way to identify resources on a device by familiar file and
     directory structure nomenclature.

Arguments:
    fdo - pointer to the device object for this instance of the Ezusb device

Return Value:
    NT status code
--*/
{
   NTSTATUS ntStatus;
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION )fdo->DeviceExtension;

   Ezusb_KdPrint(("Enter Ezusb_Create()\n"));

   if (!pdx->Started)
   {
      return STATUS_UNSUCCESSFUL;
   }

   // increment the open handle counter
   pdx->OpenHandles++;

   Irp->IoStatus.Status = STATUS_SUCCESS;
   Irp->IoStatus.Information = 0;

   // Create all the symbolic links here
   ntStatus = Irp->IoStatus.Status;

   IoCompleteRequest (Irp,
                    IO_NO_INCREMENT
                    );

   return ntStatus;

}//Ezusb_Create


NTSTATUS
Ezusb_Close(
    IN PDEVICE_OBJECT fdo,
    IN PIRP Irp
    )
/*++
Routine Description:
    Entry point for CloseHandle calls from user mode apps to close handles they have opened

Arguments:
    fdo - pointer to the device object for this instance of the Ezusb device
    Irp          - pointer to an irp

Return Value:
    NT status code

--*/
{
   NTSTATUS ntStatus;
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION )fdo->DeviceExtension;

   Ezusb_KdPrint(("Enter Ezusb_Close()\n"));

   // decrement the open handle counter
   pdx->OpenHandles--;

   Irp->IoStatus.Status = STATUS_SUCCESS;
   Irp->IoStatus.Information = 0;

   ntStatus = Irp->IoStatus.Status;

   IoCompleteRequest (Irp,
                    IO_NO_INCREMENT
                    );

   return ntStatus;

}//Ezusb_Close


NTSTATUS
Ezusb_ProcessIOCTL(
    IN PDEVICE_OBJECT fdo,
    IN PIRP Irp
    )
/*++

Routine Description:
   This where all the DeviceIoControl codes are handled.  You can add more code
   here to handle IOCTL codes that are specific to your device driver.

Arguments:
   fdo - pointer to the device object for this instance of the Ezusb device.

Return Value:
   NT status code
--*/
{
   PIO_STACK_LOCATION irpStack;
   PVOID ioBuffer;
   ULONG inputBufferLength;
   ULONG outputBufferLength;
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION )fdo->DeviceExtension;
   ULONG ioControlCode;
   NTSTATUS ntStatus;
   ULONG length;
   PUCHAR pch;

   Ezusb_KdPrint (("IRP_MJ_DEVICE_CONTROL\n"));

   if (!LockDevice(fdo))
      return CompleteRequest(Irp, STATUS_DELETE_PENDING, 0);

   //
   // Get a pointer to the current location in the Irp. This is where
   //     the function codes and parameters are located.
   //
   irpStack = IoGetCurrentIrpStackLocation (Irp);

   Irp->IoStatus.Status = STATUS_SUCCESS;
   Irp->IoStatus.Information = 0;

   ioBuffer           = Irp->AssociatedIrp.SystemBuffer;
   inputBufferLength  = irpStack->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;

   ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

   //
   // Handle Ioctls from User mode
   //
   switch (ioControlCode)
   {

      case IOCTL_Ezusb_VENDOR_REQUEST:
         length = Ezusb_VendorRequest (fdo, (PVENDOR_REQUEST_IN) ioBuffer);

         if (length)
         {
            Irp->IoStatus.Information = length;
            Irp->IoStatus.Status = STATUS_SUCCESS;
         }
         else
         {
            Irp->IoStatus.Status = STATUS_SUCCESS;
         }
   
         Ezusb_KdPrint(("Vendor Request returned %d bytes\n", length));

         break;

      case IOCTL_Ezusb_ANCHOR_DOWNLOAD:
      {
#define CHUNKLENGTH 512

         PURB                urb             = NULL;
         int   chunkcount;
         int   chunklength = 1024;
         PVOID ioBufferPtr = NULL;
         int i;

         urb = ExAllocatePool(NonPagedPool, 
                             sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

         Ezusb_KdPrint (("IOCTL_Ezusb_ANCHOR_DOWNLOAD\n"));
                
         if (urb)
         {
            chunkcount = inputBufferLength / CHUNKLENGTH;
            ioBufferPtr = ioBuffer;

            for (i=0;i<chunkcount;i++)
            {
               RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

               urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
               urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

               Ezusb_KdPrint (("ioBuffer: %x | inputBufferLength: %d | outputBufferLength: %d\n",
                                 ioBuffer, inputBufferLength, outputBufferLength));

               urb->UrbControlVendorClassRequest.TransferBufferLength = CHUNKLENGTH;
               urb->UrbControlVendorClassRequest.TransferBuffer = ioBufferPtr;
               urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
               urb->UrbControlVendorClassRequest.Request = 0xA0;
               urb->UrbControlVendorClassRequest.Value = (i * CHUNKLENGTH);
               urb->UrbControlVendorClassRequest.Index = 0;

               ntStatus = Ezusb_CallUSBD(fdo, urb);

               (char *) ioBufferPtr += CHUNKLENGTH;
            }

            ExFreePool(urb);
         }
      }

      break;

      case IOCTL_EZUSB_ANCHOR_DOWNLOAD:
      {
         PANCHOR_DOWNLOAD_CONTROL downloadControl = (PANCHOR_DOWNLOAD_CONTROL) ioBuffer;

         Ezusb_KdPrint (("IOCTL_EZUSB_ANCHOR_DOWNLOAD\n"));

         //
         // check the arguments
         //
         if (inputBufferLength != sizeof(ANCHOR_DOWNLOAD_CONTROL) ||
             outputBufferLength == 0)
         {
            Ezusb_KdPrint (("Error: Invalid Parameter\n"));
            ntStatus = STATUS_INVALID_PARAMETER;
         }
         else
         {
            ntStatus = Ezusb_AnchorDownload(fdo,
                                            downloadControl->Offset,
                                            (PUCHAR) MmGetSystemAddressForMdl(Irp->MdlAddress),
                                            outputBufferLength);
         }

         break;
      }
      case IOCTL_Ezusb_GET_CURRENT_CONFIG:

         Irp->IoStatus.Status = STATUS_SUCCESS;
         Irp->IoStatus.Information = 0;

         break;

      case IOCTL_EZUSB_GET_CURRENT_FRAME_NUMBER:
      {
         ULONG frameNumber = 0;

         //
         // make sure the output buffer is valid
         //
         if (outputBufferLength < sizeof(ULONG))
         {
            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
            break;
         }
         
         frameNumber = Ezusb_GetCurrentFrameNumber (fdo);

         if (frameNumber)
         {
            *((PULONG)ioBuffer) = frameNumber;
            Irp->IoStatus.Information = sizeof(ULONG);
            Irp->IoStatus.Status = STATUS_SUCCESS;
         }
         else
            Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
      }

         break;

      case IOCTL_Ezusb_RESETPIPE:
      {
         ULONG pipenum = *((PULONG) ioBuffer);

         Irp->IoStatus.Status = Ezusb_ResetPipe(fdo,pipenum);
      }

      break;

      case IOCTL_Ezusb_ABORTPIPE:
      {
         int pipenum = *((PULONG) ioBuffer);

         Ezusb_AbortPipe(fdo,
                        (USBD_PIPE_HANDLE) pdx->Interface->Pipes[pipenum].PipeHandle);

         Irp->IoStatus.Information = 0;
         Irp->IoStatus.Status = STATUS_SUCCESS;
      }
      
         break;

      case IOCTL_Ezusb_GET_PIPE_INFO:
         //
         // inputs  - none
         // outputs - we copy the interface information structure that we have
         //           stored in our device extension area to the output buffer which
         //           will be reflected to the user mode application by the IOS.
         //
         length = 0;
         pch = (PUCHAR) ioBuffer;

         if (pdx->Interface)
         {
            RtlCopyMemory(pch+length,
                          (PUCHAR) pdx->Interface,
                          pdx->Interface->Length);

            length += pdx->Interface->Length;
         }


         Irp->IoStatus.Information = length;
         Irp->IoStatus.Status = STATUS_SUCCESS;

         break;

      case IOCTL_Ezusb_GET_DEVICE_DESCRIPTOR:
         //
         // inputs  - pointer to a buffer in which to place descriptor data
         // outputs - we put the device descriptor data, if any is returned by the device
         //           in the system buffer and then we set the length inthe Information field
         //           in the Irp, which will then cause the system to copy the buffer back
         //           to the user's buffer
         //
         length = Ezusb_GetDeviceDescriptor (fdo, ioBuffer);

         Ezusb_KdPrint(("Get Device Descriptor returned %d bytes\n", length));

         Irp->IoStatus.Information = length;
         Irp->IoStatus.Status = STATUS_SUCCESS;

         break;

      case IOCTL_Ezusb_GET_STRING_DESCRIPTOR:
         {
            PGET_STRING_DESCRIPTOR_IN Input = ioBuffer;

            if ((inputBufferLength = sizeof(GET_STRING_DESCRIPTOR_IN)) &&
                (outputBufferLength > 0))
            {
               length = Ezusb_GetStringDescriptor (fdo,
                                                   Input->Index,
                                                   Input->LanguageId,
                                                   ioBuffer,
                                                   outputBufferLength);

               if (length)
               {
                  Irp->IoStatus.Information = length;
                  Irp->IoStatus.Status = STATUS_SUCCESS;
               }
               else
               {
                  Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
               }
            }
            else
            {
               Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
            }
         }      
         break;

         case IOCTL_Ezusb_GET_CONFIGURATION_DESCRIPTOR:
            //
            // inputs  - pointer to a buffer in which to place descriptor data
            // outputs - we put the configuration descriptor data, if any is returned by the device
            //           in the system buffer and then we set the length in the Information field
            //           in the Irp, which will then cause the system to copy the buffer back
            //           to the user's buffer
            //
            length = Ezusb_GetConfigDescriptor (fdo, ioBuffer, outputBufferLength);

            Ezusb_KdPrint(("Get Config Descriptor returned %d bytes\n", length));

            Irp->IoStatus.Information = length;
            Irp->IoStatus.Status = STATUS_SUCCESS;

            break;

      case IOCTL_Ezusb_SETINTERFACE:
      {
         PSET_INTERFACE_IN input = ioBuffer;

         Irp->IoStatus.Status = SetInterface(fdo,
                                             input->interfaceNum,
                                             input->alternateSetting);
         Irp->IoStatus.Status = 0;
      }
      break;
    
      case IOCTL_Ezusb_RESET:

         Ezusb_ResetParentPort(fdo);

         break;

      case IOCTL_EZUSB_BULK_WRITE:
      case IOCTL_EZUSB_BULK_READ:

         Irp->IoStatus.Status = Ezusb_Read_Write(fdo,Irp);

         break;

      case IOCTL_EZUSB_VENDOR_OR_CLASS_REQUEST:

         Irp->IoStatus.Status = Ezusb_VendorRequest2(fdo,Irp);

         break;

      case IOCTL_EZUSB_GET_LAST_ERROR:

         //
         // make sure the output buffer is ok, and then copy the most recent
         // URB status from the device extension to it
         //
         if (outputBufferLength >= sizeof(ULONG))
         {
            *((PULONG)ioBuffer) = pdx->LastFailedUrbStatus;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = sizeof(ULONG);
         }
         else
         {
            Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
         }

         break;

         case IOCTL_EZUSB_ISO_READ:
         case IOCTL_EZUSB_ISO_WRITE:
            Irp->IoStatus.Status = Ezusb_StartIsoTransfer(fdo,Irp);
            Irp->IoStatus.Information = 0;
         break;

         case IOCTL_EZUSB_START_ISO_STREAM:
            Irp->IoStatus.Status = Ezusb_StartIsoStream(fdo,Irp);
            Irp->IoStatus.Information = 0;
         break;

         case IOCTL_EZUSB_STOP_ISO_STREAM:
            pdx->StopIsoStream = TRUE;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = 0;
         break;

         case IOCTL_EZUSB_READ_ISO_BUFFER:
         {
            ULONG dataBytesToRead;
            ULONG descriptorBytesToRead;
            ULONG dataBytesRead;
            ULONG descriptorBytesRead;

            PISO_TRANSFER_CONTROL isoControl =
               (PISO_TRANSFER_CONTROL)Irp->AssociatedIrp.SystemBuffer;

            //
            // verify the input and output params
            //
            if (inputBufferLength != sizeof(ISO_TRANSFER_CONTROL))
            {
               Irp->IoStatus.Information = 0; 
               Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
               break;
            }

            if (outputBufferLength !=
                isoControl->PacketCount * (isoControl->PacketSize + sizeof(USBD_ISO_PACKET_DESCRIPTOR)))
            {
               Irp->IoStatus.Information = 0; 
               Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
               break;
            }

            //
            // make sure the ring buffers exist
            //
            if (!(pdx->DataRingBuffer && pdx->DescriptorRingBuffer))
            {
               Irp->IoStatus.Information = 0; 
               Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
               break;
            }

            dataBytesToRead = isoControl->PacketCount * isoControl->PacketSize;
            dataBytesRead = ReadRingBuffer(pdx->DataRingBuffer,
                                           MmGetSystemAddressForMdl(Irp->MdlAddress),
                                           dataBytesToRead);
            Ezusb_KdPrint(("Copied %d bytes from the data ring buffer\n",dataBytesRead));

            descriptorBytesToRead = (dataBytesRead / isoControl->PacketSize) * sizeof(USBD_ISO_PACKET_DESCRIPTOR);
            descriptorBytesRead = ReadRingBuffer(pdx->DescriptorRingBuffer,
                                           ((PUCHAR) MmGetSystemAddressForMdl(Irp->MdlAddress)) + dataBytesRead,
                                           descriptorBytesToRead);
            Ezusb_KdPrint(("Copied %d bytes from the desc ring buffer\n",descriptorBytesRead));

            Irp->IoStatus.Information = dataBytesRead + descriptorBytesRead;
            Irp->IoStatus.Status = STATUS_SUCCESS;
         }
         break;

         case IOCTL_EZUSB_GET_DRIVER_VERSION:
         {
            PEZUSB_DRIVER_VERSION version = (PEZUSB_DRIVER_VERSION) ioBuffer;

            if (outputBufferLength >= sizeof(EZUSB_DRIVER_VERSION))
            {
               version->MajorVersion = EZUSB_MAJOR_VERSION;
               version->MinorVersion = EZUSB_MINOR_VERSION;
               version->BuildVersion = EZUSB_BUILD_VERSION;

               Irp->IoStatus.Status = STATUS_SUCCESS;
               Irp->IoStatus.Information = sizeof(EZUSB_DRIVER_VERSION);
            }
            else
            {
               Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
            }
         }
         break;

         case IOCTL_EZUSB_SET_FEATURE:
         {
            //
            // verify the input params
            //
            if (inputBufferLength != sizeof(SET_FEATURE_CONTROL))
            {
               Irp->IoStatus.Information = 0; 
               Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
               break;
            }

            Ezusb_SetFeature(fdo,(PSET_FEATURE_CONTROL)Irp->AssociatedIrp.SystemBuffer);
            Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
         }
         break;

      default:

      Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
   }

   UnlockDevice(fdo);

   ntStatus = Irp->IoStatus.Status;

   IoCompleteRequest (Irp,IO_NO_INCREMENT);

   return ntStatus;

}


第三部分:

ULONG
Ezusb_GetDescriptor(
    IN PDEVICE_OBJECT fdo,
    PVOID             DescriptorBuffer,
    ULONG             BufferLength,
    UCHAR             DescriptorType
    )
/*++
Routine Description:
    Gets a device descriptor from the given device object
    
Arguments:
    fdo - pointer to our device object
    DescriptorBuffer - buffer to accept the descriptor table
    BufferLength - size of the allocated Descriptor Buffer
    DescriptorType - Descriptor Type (per USB spec)

Return Value:
    Number of valid bytes in data buffer  

--*/
{
   NTSTATUS            ntStatus        = STATUS_SUCCESS;
   PURB                urb             = NULL;
   ULONG               length          = 0;
   PDEVICE_EXTENSION   pdx = NULL;

   Ezusb_KdPrint (("Enter Ezusb_GetDescriptor\n"));    

   pdx = fdo->DeviceExtension;

   urb = ExAllocatePool(NonPagedPool, 
                      sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
                   
   if (urb)
   {
      if (DescriptorBuffer)
      {    
         UsbBuildGetDescriptorRequest(urb,
                                      (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                      DescriptorType,                //descriptor type
                                      0,                             //index
                                      0,                             //language ID
                                      DescriptorBuffer,              //transfer buffer
                                      NULL,                          //MDL
                                      BufferLength,                  //buffer length
                                      NULL);                         //link
                                                                  
         ntStatus = Ezusb_CallUSBD(fdo, urb);

         // If successful, get the length from the Urb
         if (NT_SUCCESS(ntStatus))
         {
            length = urb->UrbControlDescriptorRequest.TransferBufferLength;
         }
      }
      else
      {
         ntStatus = STATUS_INVALID_PARAMETER;
      }    

      Ezusb_KdPrint (("%d bytes of descriptor received\n",length));
      ExFreePool(urb);
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;        
   }        

   // If unsuccessful, set the length to 0
   if (!(NT_SUCCESS(ntStatus)))
   {
      length = 0;
   }
    
   Ezusb_KdPrint (("Leaving Ezusb_GetDescriptor\n"));    

   return length;
}    

ULONG
Ezusb_GetDeviceDescriptor(
    IN PDEVICE_OBJECT fdo,
    PVOID             pvOutputBuffer
    )
/*++
Routine Description:
    Gets a device descriptor from the given device object
    
Arguments:
    fdo - pointer to the sample device object

Return Value:
    Number of valid bytes in data buffer  

--*/
{
   return (Ezusb_GetDescriptor(fdo,
                               pvOutputBuffer,
                               sizeof(USB_DEVICE_DESCRIPTOR),
                               USB_DEVICE_DESCRIPTOR_TYPE));
}    


ULONG
Ezusb_GetConfigDescriptor(
    IN PDEVICE_OBJECT fdo,
    PVOID             pvOutputBuffer,
    ULONG             ulLength
    )
/*++

Routine Description:
    Gets a configuration descriptor from the given device object
    
Arguments:
    fdo    - pointer to the sample device object
    pvOutputBuffer  - pointer to the buffer where the data is to be placed
    ulLength        - length of the buffer

Return Value:
    Number of valid bytes in data buffer  

--*/
{
   return (Ezusb_GetDescriptor(fdo,
                               pvOutputBuffer,
                               ulLength,
                               USB_CONFIGURATION_DESCRIPTOR_TYPE));
}    

ULONG
Ezusb_GetOtherSpeedConfigDescriptor(
    IN PDEVICE_OBJECT fdo,
    PVOID             pvOutputBuffer,
    ULONG             ulLength
    )
/*++

Routine Description:
    Gets a configuration descriptor from the given device object
    
Arguments:
    fdo    - pointer to the sample device object
    pvOutputBuffer  - pointer to the buffer where the data is to be placed
    ulLength        - length of the buffer

Return Value:
    Number of valid bytes in data buffer  

--*/
{
   return (Ezusb_GetDescriptor(fdo,
                               pvOutputBuffer,
                               ulLength,
                               7));
}    

ULONG
Ezusb_GetStringDescriptor(
    IN PDEVICE_OBJECT fdo,
    UCHAR             Index,
    USHORT            LanguageId,
    PVOID             pvOutputBuffer,
    ULONG             ulLength
    )
/*++

Routine Description:
    Gets the specified string descriptor from the given device object
    
Arguments:
    fdo    - pointer to the sample device object
    Index           - Index of the string descriptor
    LanguageId      - Language ID of the string descriptor
    pvOutputBuffer  - pointer to the buffer where the data is to be placed
    ulLength        - length of the buffer

Return Value:
    Number of valid bytes in data buffer  

--*/
{
   NTSTATUS            ntStatus        = STATUS_SUCCESS;
   PURB                urb             = NULL;
   ULONG               length          = 0;

   Ezusb_KdPrint (("Enter Ezusb_GetStringDescriptor\n"));    

   urb = ExAllocatePool(NonPagedPool, 
                      sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));
                         
   if (urb)
   {
      if (pvOutputBuffer)
      {    

         UsbBuildGetDescriptorRequest(urb,
                                      (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                      USB_STRING_DESCRIPTOR_TYPE,    //descriptor type
                                      Index,                         //index
                                      LanguageId,                    //language ID
                                      pvOutputBuffer,                //transfer buffer
                                      NULL,                          //MDL
                                      ulLength,                      //buffer length
                                      NULL);                         //link
                                                                  
         ntStatus = Ezusb_CallUSBD(fdo, urb);

      }
      else
      {
         ntStatus = STATUS_INVALID_PARAMETER;
      }    

      // If successful, get the length from the Urb, otherwise set length to 0
      if (NT_SUCCESS(ntStatus))
      {
         length = urb->UrbControlDescriptorRequest.TransferBufferLength;
      }
      else
         length = 0;

      Ezusb_KdPrint (("%d bytes of string descriptor received\n",length));

      ExFreePool(urb);
        
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;        
   }        
    
   Ezusb_KdPrint (("Leaving Ezusb_GetStringDescriptor\n"));    

   return length;
}    

NTSTATUS
Ezusb_VendorRequest2(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   NTSTATUS                   ntStatus;
   PIO_STACK_LOCATION         irpStack = IoGetCurrentIrpStackLocation (Irp);
   PVENDOR_OR_CLASS_REQUEST_CONTROL  requestControl =
                              (PVENDOR_OR_CLASS_REQUEST_CONTROL) Irp->AssociatedIrp.SystemBuffer;
   ULONG                      bufferLength =
                              irpStack->Parameters.DeviceIoControl.OutputBufferLength;
   PURB                       urb = NULL;
   ULONG                      urbSize = 0;

   ULONG                      transferFlags = 0;
   USHORT                     urbFunction = 0;

   //
   // verify that the input parameter is correct (or at least that it's
   // the right size
   //
   if (irpStack->Parameters.DeviceIoControl.InputBufferLength !=
       sizeof(VENDOR_OR_CLASS_REQUEST_CONTROL))
   {
      ntStatus = STATUS_INVALID_PARAMETER;
      return ntStatus;
   }

   //
   // allocate and fill in the Usb request (URB)
   //
   urbSize = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);

   urb = ExAllocatePool(NonPagedPool,urbSize);

   if (!urb)
   {
      return STATUS_NO_MEMORY;
   }

   RtlZeroMemory(urb,urbSize);

   transferFlags = USBD_SHORT_TRANSFER_OK;

   //
   // get direction info from the input parms
   //
   if (requestControl->direction)
      transferFlags |= USBD_TRANSFER_DIRECTION_IN;

   //
   // the type of request (class or vendor) and the recepient
   // (device, interface, endpoint, other) combine to determine the 
   // URB function.  The following ugly code transforms fields in
   // the input param into an URB function
   //
   switch ((requestControl->requestType << 2) | requestControl->recepient)
   {
      case 0x04:
         urbFunction = URB_FUNCTION_CLASS_DEVICE;
         break;
      case 0x05:
         urbFunction = URB_FUNCTION_CLASS_INTERFACE;
         break;
      case 0x06:
         urbFunction = URB_FUNCTION_CLASS_ENDPOINT;
         break;
      case 0x07:
         urbFunction = URB_FUNCTION_CLASS_OTHER;
         break;
      case 0x08:
         urbFunction = URB_FUNCTION_VENDOR_DEVICE;
         break;
      case 0x09:
         urbFunction = URB_FUNCTION_VENDOR_INTERFACE;
         break;
      case 0x0A:
         urbFunction = URB_FUNCTION_VENDOR_ENDPOINT;
         break;
      case 0x0B:
         urbFunction = URB_FUNCTION_VENDOR_OTHER;
         break;
      default:
         return STATUS_INVALID_PARAMETER;
   }

   urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
   urb->UrbHeader.Function = urbFunction;

   urb->UrbControlVendorClassRequest.TransferFlags = transferFlags;
   urb->UrbControlVendorClassRequest.TransferBufferLength = bufferLength;
   urb->UrbControlVendorClassRequest.TransferBufferMDL = Irp->MdlAddress;
   urb->UrbControlVendorClassRequest.Request = requestControl->request;
   urb->UrbControlVendorClassRequest.Value = requestControl->value;
   urb->UrbControlVendorClassRequest.Index = requestControl->index;

   //
   // Call the USB Stack.
   //
	ntStatus = Ezusb_CallUSBD(fdo, urb);

   //
   // If the transfer was successful, report the length of the transfer to the
   // caller by setting IoStatus.Information
   //
   if (NT_SUCCESS(ntStatus))
   {
      Irp->IoStatus.Information = urb->UrbControlVendorClassRequest.TransferBufferLength;
      Ezusb_KdPrint(("Successfully transfered 0x%x bytes\n",Irp->IoStatus.Information));
   }

   if (urb)
      ExFreePool(urb);

   return ntStatus;
}


ULONG
Ezusb_VendorRequest(
    IN PDEVICE_OBJECT fdo,
    IN PVENDOR_REQUEST_IN pVendorRequest
    )
{
    NTSTATUS            ntStatus        = STATUS_SUCCESS;
    PURB                urb             = NULL;
    ULONG               length          = 0;
    PUCHAR buffer = NULL;
    
    Ezusb_KdPrint (("Enter Ezusb_VendorRequest - yahoooo\n"));    

    urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
                         
    if (urb)
    {
        RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

        //
        // fill in the URB
        //
        urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
        urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

        urb->UrbControlVendorClassRequest.TransferBufferLength = pVendorRequest->wLength;
        urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
        urb->UrbControlVendorClassRequest.Request = pVendorRequest->bRequest;
        urb->UrbControlVendorClassRequest.Value = pVendorRequest->wValue;
        urb->UrbControlVendorClassRequest.Index = pVendorRequest->wIndex;


        //
        // very kludgey.  The idea is: if its an IN then a buffer has been passed
        // in from user mode.  So, use the pointer to the system buffer as the transfer
        // buffer.  If the transfer is an out, then we need to allocate a transfer 
        // buffer.  If the length of the transfer is 1, then put pVendorRequest->bData
        // in the buffer.  Otherwise, fill the buffer with an incrementing byte pattern.
        // yuch
        //
        if (pVendorRequest->direction)
        {
            urb->UrbControlVendorClassRequest.TransferFlags |= USBD_TRANSFER_DIRECTION_IN;
            urb->UrbControlVendorClassRequest.TransferBuffer = pVendorRequest;

        }
        else
        {
            urb->UrbControlVendorClassRequest.TransferFlags = 0;
            buffer = ExAllocatePool(NonPagedPool, pVendorRequest->wLength);

            urb->UrbControlVendorClassRequest.TransferBuffer = buffer;

            if (pVendorRequest->wLength == 1)
            {
                buffer[0] = pVendorRequest->bData;
            }
            else
            {
                int i;
                PUCHAR ptr = buffer;

                for (i=0; i < pVendorRequest->wLength; i++)
                {
                    *ptr = (UCHAR) i;
                    ptr++;
                }
            }
        }

        ntStatus = Ezusb_CallUSBD(fdo, urb);

        //
        // only return a length if this was an IN transaction
        //
        if (pVendorRequest->direction)
        {
            length = urb->UrbControlVendorClassRequest.TransferBufferLength;
        }
        else
        {
            length = 0;
        }

        ExFreePool(urb);
        if (buffer)
            ExFreePool(buffer);
    }
    return length;
}

NTSTATUS
Ezusb_ResetPipe(
   IN PDEVICE_OBJECT fdo,
   ULONG PipeNum
   )
/*++

Routine Description:

   Reset a given USB pipe.
   NOTE: this will reset the data toggle on the host as well

Arguments:

Return Value:


--*/
{
   NTSTATUS ntStatus;
   PDEVICE_EXTENSION             pdx = fdo->DeviceExtension;
   PUSBD_INTERFACE_INFORMATION   interfaceInfo = NULL;
   USBD_PIPE_HANDLE              pipeHandle = NULL;
   PURB urb;
   USBD_VERSION_INFORMATION VersionInformation;

   Ezusb_KdPrint (("EZUSB.SYS: Reset Pipe \n"));

   //
   // verify that the selected pipe is valid, and get a handle to it. If anything
   // is wrong, return an error
   //
   interfaceInfo = pdx->Interface;

   if (!interfaceInfo)
   {
      Ezusb_KdPrint(("Ezusb_ResetPipe() no interface info - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }
   
   if (PipeNum > interfaceInfo->NumberOfPipes)
   {
      Ezusb_KdPrint(("Ezusb_ResetPipe() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeHandle = interfaceInfo->Pipes[PipeNum].PipeHandle;

   urb = ExAllocatePool(NonPagedPool,
                      sizeof(struct _URB_PIPE_REQUEST));

   if (urb)
   {
      urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
      urb->UrbPipeRequest.PipeHandle = pipeHandle;

      //
      // kludge.  Win98 changed the size of the URB_PIPE_REQUEST.
      // Check the USBDI version.  If it is prior to win98 (0x101)
      // make the structure smaller.
      // 
      USBD_GetUSBDIVersion(&VersionInformation);
      if (VersionInformation.USBDI_Version < 0x101) 
      {
         Ezusb_KdPrint(("Ezusb_ResetPipe() Detected OSR2.1\n"));
         urb->UrbHeader.Length -= sizeof(ULONG);
      }

      ntStatus = Ezusb_CallUSBD(fdo, urb);

      ExFreePool(urb);
   }
   else
   {
      ntStatus = STATUS_INSUFFICIENT_RESOURCES;
   }

   return ntStatus;
}

NTSTATUS
Ezusb_AbortPipe(
    IN PDEVICE_OBJECT fdo,
    IN USBD_PIPE_HANDLE PipeHandle
    )
/*++

Routine Description:

   cancel pending transfers for a pipe

Arguments:

Return Value:


--*/
{
   NTSTATUS ntStatus;
   PURB urb;
   USBD_VERSION_INFORMATION VersionInformation;

   Ezusb_KdPrint (("'EZUSB.SYS: Reset Pipe \n"));

   urb = ExAllocatePool(NonPagedPool,
                      sizeof(struct _URB_PIPE_REQUEST));

   if (urb)
   {
      RtlZeroMemory(urb,sizeof(struct _URB_PIPE_REQUEST));
      urb->UrbHeader.Length = (USHORT) sizeof (struct _URB_PIPE_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
      urb->UrbPipeRequest.PipeHandle = PipeHandle;

      //
      // kludge.  Win98 changed the size of the URB_PIPE_REQUEST.
      // Check the USBDI version.  If it is prior to win98 (0x101)
      // make the structure smaller.
      // 
      USBD_GetUSBDIVersion(&VersionInformation);
      if (VersionInformation.USBDI_Version < 0x101) 
      {
         Ezusb_KdPrint(("Ezusb_ResetPipe() Detected OSR2.1\n"));
         urb->UrbHeader.Length -= sizeof(ULONG);
      }

      ntStatus = Ezusb_CallUSBD(fdo, urb);

      ExFreePool(urb);
   }
   else
   {
      ntStatus = STATUS_INSUFFICIENT_RESOURCES;
   }


   return ntStatus;
}


ULONG
Ezusb_GetCurrentFrameNumber(
    IN PDEVICE_OBJECT fdo
    )
{
   PURB     urb = NULL;
   PDEVICE_EXTENSION   pdx = NULL;
   NTSTATUS            ntStatus        = STATUS_SUCCESS;
   ULONG               frameNumber = 0;
    
   Ezusb_KdPrint (("Enter Ezusb_GetCurrentFrameNumber\n"));    

   pdx = fdo->DeviceExtension;

   urb = ExAllocatePool(NonPagedPool,sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER));

   if (urb == NULL)
      return 0;

   RtlZeroMemory(urb,sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER));

   urb->UrbHeader.Length = sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER);
   urb->UrbHeader.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;

   ntStatus = Ezusb_CallUSBD(fdo, urb);

   if (NT_SUCCESS(ntStatus))
   {
      frameNumber = urb->UrbGetCurrentFrameNumber.FrameNumber;
   }

   ExFreePool(urb);

   return frameNumber;
}

NTSTATUS
Ezusb_ResetParentPort(
    IN IN PDEVICE_OBJECT fdo
    )
/*++

Routine Description:

    Reset the our parent port

Arguments:

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    NTSTATUS ntStatus, status = STATUS_SUCCESS;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;
    PDEVICE_EXTENSION pdx;

    Ezusb_KdPrint (("EZUSB.SYS: enter Ezusb_ResetPort\n"));

    pdx = fdo->DeviceExtension;

    //
    // issue a synchronous request
    //

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    irp = IoBuildDeviceIoControlRequest(
                IOCTL_INTERNAL_USB_RESET_PORT,
                pdx->StackDeviceObject,
//                pdx->TopOfStackDeviceObject,
                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.
    //

    nextStack = IoGetNextIrpStackLocation(irp);
    ASSERT(nextStack != NULL);

    Ezusb_KdPrint (("EZUSB.SYS: calling USBD enable port api\n"));

    ntStatus = IoCallDriver(pdx->StackDeviceObject,
                            irp);
                            
    Ezusb_KdPrint (("EZUSB.SYS: return from IoCallDriver USBD %x\n", ntStatus));

    if (ntStatus == STATUS_PENDING) {

        Ezusb_KdPrint (("EZUSB.SYS: Wait for single object\n"));

        status = KeWaitForSingleObject(
                       &event,
                       Suspended,
                       KernelMode,
                       FALSE,
                       NULL);

        Ezusb_KdPrint (("EZUSB.SYS: Wait for single object, returned %x\n", status));
        
    } else {
        ioStatus.Status = ntStatus;
    }

    //
    // USBD maps the error code for us
    //
    ntStatus = ioStatus.Status;

    Ezusb_KdPrint (("EZUSB.SYS: Ezusb_ResetPort (%x)\n", ntStatus));

    return ntStatus;
}

ULONG
Ezusb_DownloadTest(
    IN PDEVICE_OBJECT fdo,
    IN PVENDOR_REQUEST_IN pVendorRequest
    )
{
    NTSTATUS            ntStatus        = STATUS_SUCCESS;
    PURB                urb             = NULL;
    ULONG               length          = 0;
    ULONG i;
    PUCHAR  buffer1 = NULL;
    PUCHAR  buffer2 = NULL;
    ULONG CompareCount = 0;
    
    Ezusb_KdPrint (("Enter Ezusb_VendorRequest - yahoooo\n"));    

    urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

    buffer1 = ExAllocatePool(NonPagedPool, CHUNKLENGTH);
    buffer2 = ExAllocatePool(NonPagedPool, CHUNKLENGTH);

    for (i=0; i < CHUNKLENGTH; i++)
    {
        buffer1[i] = (UCHAR) i;
    }

    if (urb)
    {
        for (i=0; i < 5120 / CHUNKLENGTH; i++)
        {
            RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

            //
            // fill in the URB
            //
            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
            urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

            urb->UrbControlVendorClassRequest.TransferBufferLength = CHUNKLENGTH;
            urb->UrbControlVendorClassRequest.TransferBuffer = buffer1;
            urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
            urb->UrbControlVendorClassRequest.Request = 0xA0;
            urb->UrbControlVendorClassRequest.Value = (unsigned short) i * CHUNKLENGTH;
            urb->UrbControlVendorClassRequest.Index = 0;

            ntStatus = Ezusb_CallUSBD(fdo, urb);

        }

        for (i=0; i < 5120 / CHUNKLENGTH; i++)
        {
            RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));
            RtlZeroMemory(buffer2, CHUNKLENGTH);

            //
            // fill in the URB
            //
            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
            urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

            urb->UrbControlVendorClassRequest.TransferBufferLength = CHUNKLENGTH;
            urb->UrbControlVendorClassRequest.TransferBuffer = buffer2;
            urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
            urb->UrbControlVendorClassRequest.Request = 0xA0;
            urb->UrbControlVendorClassRequest.Value = (unsigned short) i * CHUNKLENGTH;
            urb->UrbControlVendorClassRequest.Index = 0;
            urb->UrbControlVendorClassRequest.TransferFlags |= USBD_TRANSFER_DIRECTION_IN;

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            CompareCount = RtlCompareMemory(buffer1,buffer2,CHUNKLENGTH);
            Ezusb_KdPrint (("%d matched\n",CompareCount))
            if (CompareCount != CHUNKLENGTH)
            {
                Ezusb_KdPrint (("**** Compare Error *****************************************************\n"));
            }


        }

    }
    return length;
}


PUSB_CONFIGURATION_DESCRIPTOR
GetConfigDescriptor(
    IN PDEVICE_OBJECT fdo
    )
/*++

Routine Description:

Arguments:



Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION pdx;
    NTSTATUS ntStatus;
    PURB urb;
    ULONG siz;
    PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;

    Ezusb_KdPrint (("Ezusb.SYS: enter Ezusb_GetConfigDescriptor\n"));

    pdx = fdo->DeviceExtension;

    urb = ExAllocatePool(NonPagedPool,
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST));

    if (urb) {


        siz = sizeof(USB_CONFIGURATION_DESCRIPTOR)+256;

get_config_descriptor_retry2:

        configurationDescriptor = ExAllocatePool(NonPagedPool,
                                                 siz);

        if (configurationDescriptor) {

            UsbBuildGetDescriptorRequest(urb,
                                         (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
                                         USB_CONFIGURATION_DESCRIPTOR_TYPE,
                                         0,
                                         0,
                                         configurationDescriptor,
                                         NULL,
                                         siz,
                                         NULL);

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            Ezusb_KdPrint (("Ezusb.SYS: Configuration Descriptor = %x, len %x\n",
                            configurationDescriptor,
                            urb->UrbControlDescriptorRequest.TransferBufferLength));
        } else {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        }

        if (NT_SUCCESS(ntStatus) &&
            (urb->UrbControlDescriptorRequest.TransferBufferLength >= 
             sizeof(USB_CONFIGURATION_DESCRIPTOR)) &&
            (configurationDescriptor->wTotalLength >=
             sizeof(USB_CONFIGURATION_DESCRIPTOR)))
        {
            //
            // The Get Config Descriptor request did not return an error
            // AND at least enough data was transferred to fill a Config
            // Descriptor AND the Config Descriptor wLength is at least the
            // size of a Config Descriptor
            //
            if (configurationDescriptor->wTotalLength > siz)
            {
                //
                // The request buffer is not big enough to hold the
                // entire set of descriptors.  Free the current buffer
                // and retry with a buffer which should be big enough.
                //
                siz = configurationDescriptor->wTotalLength;
                ExFreePool(configurationDescriptor);
                configurationDescriptor = NULL;
                goto get_config_descriptor_retry2;
            }
            else if (configurationDescriptor->wTotalLength >
                     urb->UrbControlDescriptorRequest.TransferBufferLength)
            {
                //
                // The request buffer is greater than or equal to the
                // Config Descriptor wLength, but less data was transferred
                // than wLength.  Return NULL to indicate a device error.
                //
                ExFreePool(configurationDescriptor);
                configurationDescriptor = NULL;
            }
            //
            // else  everything is OK with the Config Descriptor, return it.
            //
        }
        else
        {
            //
            // The Get Config Descriptor request returned an error OR
            // not enough data was transferred to fill a Config Descriptor
            // OR the Config Descriptor wLength is less than the size of
            // a Config Descriptor.  Return NULL to indicate a device error.
            //
            ExFreePool(configurationDescriptor);
            configurationDescriptor = NULL;
        }

        ExFreePool(urb);

    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    Ezusb_KdPrint (("Ezusb.SYS: exit Ezusb_GetConfigDescriptor\n"));

    return configurationDescriptor;
}


第四部分:

NTSTATUS
ConfigureDevice(
    IN  PDEVICE_OBJECT fdo
    )
/*++
Routine Description:
    Configures the USB device via USB-specific device requests and interaction
    with the USB software subsystem.

Arguments:
    fdo - pointer to the device object for this instance of the Ezusb Device

Return Value:
    NT status code
--*/
{
   PDEVICE_EXTENSION pdx = fdo->DeviceExtension;
   NTSTATUS ntStatus;
   PURB urb = NULL;
   ULONG numberOfPipes;
   PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;
   PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;

   Ezusb_KdPrint (("enter ConfigureDevice\n"));

   //
   // Get the configuration Descriptor
   //
   configurationDescriptor = GetConfigDescriptor(fdo);

   if (!configurationDescriptor)
   {
      Ezusb_KdPrint (("Get Configuration Descriptor Failed\n"));
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupConfigureDevice;
   }

#define INTERFACENUMBER 0
#define ALTERNATESETTING 1
   //
   // Get the interface Descriptor for the interface we want
   //
   interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
                           configurationDescriptor,
                           configurationDescriptor,
                           INTERFACENUMBER,
                           ALTERNATESETTING,
                           -1,
                           -1,
                           -1);
   if (!interfaceDescriptor)
   {
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupConfigureDevice;
   }

   numberOfPipes = interfaceDescriptor->bNumEndpoints;
   
   //
   // Configure the Device, but don't select any interfaces
   //
   urb = USBD_CreateConfigurationRequestEx(configurationDescriptor, NULL);

   if (!urb)
   {
      Ezusb_KdPrint (("USBD_CreateConfigurationRequestEx Failed\n"));
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupConfigureDevice;
   }

   ntStatus = Ezusb_CallUSBD(fdo, urb);

   if (NT_SUCCESS(ntStatus))
   {
      pdx->ConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle;
   }
   else
   {
      Ezusb_KdPrint (("Configuration Request Failed\n"));
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupConfigureDevice;
   }

CleanupConfigureDevice:

   // Clean up and exit this routine
   if (urb != NULL)
   {
      ExFreePool(urb);
   }

   if (configurationDescriptor != NULL)
   {
      ExFreePool(configurationDescriptor);
   }

    Ezusb_KdPrint (("exit Ezusb_ConfigureDevice (%x)\n", ntStatus));
    return ntStatus;
}


NTSTATUS
SetInterface(
   IN PDEVICE_OBJECT fdo,
   IN UCHAR InterfaceNumber,
   IN UCHAR AlternateSetting
   )
{
   PUSBD_INTERFACE_INFORMATION interfaceInformation = NULL;
   PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;
   PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL;
   PDEVICE_EXTENSION pdx = fdo->DeviceExtension;
   PURB urb = NULL;
   USHORT urbSize;
   ULONG numberOfPipes;
   ULONG i,j;
   NTSTATUS ntStatus;

   //
   // Get the configuration Descriptor
   //
   configurationDescriptor = GetConfigDescriptor(fdo);
   if (!configurationDescriptor)
   {
      Ezusb_KdPrint (("Get Configuration Descriptor Failed\n"));
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupSetInterface;
   }

   //
   // Get the interface Descriptor for the interface we want
   //
   interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
                           configurationDescriptor,
                           configurationDescriptor,
                           InterfaceNumber,
                           AlternateSetting,
                           -1,
                           -1,
                           -1);

   if (!interfaceDescriptor)
   {
      ntStatus = STATUS_UNSUCCESSFUL;
      goto CleanupSetInterface;
   }

   numberOfPipes = interfaceDescriptor->bNumEndpoints;
   Ezusb_KdPrint (("numberOfPipes = %d\n", numberOfPipes));


   urbSize = (USHORT)GET_SELECT_INTERFACE_REQUEST_SIZE(numberOfPipes);
   Ezusb_KdPrint (("urbSize = %d\n", urbSize));
   urb = ExAllocatePool(NonPagedPool,urbSize);

   if (!urb)
   {
      ntStatus = STATUS_NO_MEMORY;
      goto CleanupSetInterface;
   }
   
   RtlZeroMemory(urb,urbSize);

   UsbBuildSelectInterfaceRequest(urb,
                                  urbSize,
                                  pdx->ConfigurationHandle,
                                  InterfaceNumber,
                                  AlternateSetting);


   interfaceInformation = &(urb->UrbSelectInterface.Interface);
   interfaceInformation->Length = (USHORT)GET_USBD_INTERFACE_SIZE(numberOfPipes);

   for (i = 0 ;i < numberOfPipes ;i++ )
   {
      interfaceInformation->Pipes[i].MaximumTransferSize = (64*1024) -1;
   }

   ntStatus = Ezusb_CallUSBD(fdo, urb);

   //
   // If that succeeded, then update the Interface structure
   // in the device extension.
   //
   if (NT_SUCCESS(ntStatus))
   {
      for (j=0; j<interfaceInformation->NumberOfPipes; j++)
      {
         PUSBD_PIPE_INFORMATION pipeInformation;

         pipeInformation = &interfaceInformation->Pipes[j];

         Ezusb_KdPrint (("---------\n"));
         Ezusb_KdPrint (("PipeType 0x%x\n", pipeInformation->PipeType));
         Ezusb_KdPrint (("EndpointAddress 0x%x\n", pipeInformation->EndpointAddress));
         Ezusb_KdPrint (("MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize));
         Ezusb_KdPrint (("Interval 0x%x\n", pipeInformation->Interval));
         Ezusb_KdPrint (("Handle 0x%x\n", pipeInformation->PipeHandle));
         Ezusb_KdPrint (("MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize));
      }

      if (pdx->Interface)
      {
         ExFreePool(pdx->Interface);
      }

      pdx->Interface = NULL;
      pdx->Interface = ExAllocatePool(NonPagedPool,interfaceInformation->Length);
      if (!pdx->Interface)
      {
         ntStatus = STATUS_NO_MEMORY;
         goto CleanupSetInterface;
      }
      RtlCopyMemory(pdx->Interface, interfaceInformation, interfaceInformation->Length);
   }

CleanupSetInterface:

   // Clean up and exit this routine
   if (urb != NULL)
   {
      ExFreePool(urb);
   }

   if (configurationDescriptor != NULL)
   {
      ExFreePool(configurationDescriptor);
   }

   return(ntStatus);
   
}   

#define BYTES_PER_LINE 0x10

void
DumpBuffer(PVOID pvBuffer, ULONG length)
{
    int                    nItems    = 0;
    char                   temp[64]  = "";
    char                   temp2[64]  = "";
	ULONG	i;
	ULONG   j;
	PUCHAR	ptr;


	ptr = (PUCHAR) pvBuffer;

	for (i = 0; i < ((length + BYTES_PER_LINE - 1) / BYTES_PER_LINE); i++)
	{
		sprintf(temp,"%04X ",(i*BYTES_PER_LINE));
		for (j = 0; j < BYTES_PER_LINE; j++)
		{
			if (((i * BYTES_PER_LINE) + j) < length)
			{
				sprintf(temp2,"%02X ",*ptr++);
				strcat(temp,temp2);
			}
		}
//	    SendMessage (hOutputBox, LB_ADDSTRING, 0, (LPARAM)temp);
         Ezusb_KdPrint (("%s\n",temp));
	}
}



///
// @func Lock a SIMPLE device object
// @parm Address of our device extension
// @rdesc TRUE if it was possible to lock the device, FALSE otherwise.
// @comm A FALSE return value indicates that we're in the process of deleting
// the device object, so all new requests should be failed

BOOLEAN LockDevice(
   IN PDEVICE_OBJECT fdo
   )
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

   // Increment use count on our device object
   LONG usage = InterlockedIncrement(&pdx->usage);

   // AddDevice initialized the use count to 1, so it ought to be bigger than
   // one now. HandleRemoveDevice sets the "removing" flag and decrements the
   // use count, possibly to zero. So if we find a use count of "1" now, we
   // should also find the "removing" flag set.

   ASSERT(usage > 1 || pdx->removing);

   // If device is about to be removed, restore the use count and return FALSE.
   // If we're in a race with HandleRemoveDevice (maybe running on another CPU),
   // the sequence we've followed is guaranteed to avoid a mistaken deletion of
   // the device object. If we test "removing" after HandleRemoveDevice sets it,
   // we'll restore the use count and return FALSE. In the meantime, if
   // HandleRemoveDevice decremented the count to 0 before we did our increment,
   // its thread will have set the remove event. Otherwise, we'll decrement to 0
   // and set the event. Either way, HandleRemoveDevice will wake up to finish
   // removing the device, and we'll return FALSE to our caller.
   // 
   // If, on the other hand, we test "removing" before HandleRemoveDevice sets it,
   // we'll have already incremented the use count past 1 and will return TRUE.
   // Our caller will eventually call UnlockDevice, which will decrement the use
   // count and might set the event HandleRemoveDevice is waiting on at that point.

   if (pdx->removing)
	{
	   if (InterlockedDecrement(&pdx->usage) == 0)
		   KeSetEvent(&pdx->evRemove, 0, FALSE);
	   return FALSE;
	}

   return TRUE;
}

///
// @func Unlock a SIMPLE device object
// @parm Address of our device extension
// @comm If the use count drops to zero, set the evRemove event because we're
// about to remove this device object.

void UnlockDevice(
   PDEVICE_OBJECT fdo
   )
{
   PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
   LONG usage = InterlockedDecrement(&pdx->usage);

   ASSERT(usage >= 0);

   if (usage == 0)
   {						// removing device
      ASSERT(pdx->removing);	// HandleRemoveDevice should already have set this
      KeSetEvent(&pdx->evRemove, 0, FALSE);
   }						// removing device
}

NTSTATUS IsoTransferComplete(
   IN PDEVICE_OBJECT bunkfdo,
   IN PIRP Irp,
   IN PVOID Context
   )
{
   NTSTATUS ntStatus,status;
   PISO_TRANSFER_OBJECT transferObject = (PISO_TRANSFER_OBJECT) Context;
   PISO_STREAM_OBJECT streamObject = transferObject->StreamObject;
   PDEVICE_OBJECT fdo = streamObject->DeviceObject;
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   PIO_STACK_LOCATION nextStack;
   PURB urb = transferObject->Urb;
   USHORT urbSize = 0;
   ULONG i;
   

   Ezusb_KdPrint(("IsoTransferComplete Irp Status 0x%x\n",Irp->IoStatus.Status));

   //
   // copy the ISO transfer descriptors to the callers buffer.  The user mode caller passed
   // in a large buffer.  That buffer holds both the data and also provides space for the ISO
   // descriptors.  This way, the caller can verify the status of each ISO packet transmitted
   // or sent.
   //
   RtlCopyMemory((PUCHAR) streamObject->IsoDescriptorBuffer + (transferObject->Frame * sizeof(USBD_ISO_PACKET_DESCRIPTOR)),
                 urb->UrbIsochronousTransfer.IsoPacket,
                 (streamObject->FramesPerBuffer * sizeof(USBD_ISO_PACKET_DESCRIPTOR)));

   for (i=0;i<streamObject->FramesPerBuffer; i++)
   {
      Ezusb_KdPrint (("Packet %d length = %d status = %d\n",i,
         urb->UrbIsochronousTransfer.IsoPacket[i].Length,urb->UrbIsochronousTransfer.IsoPacket[i].Status));
   }
      
   
   transferObject->Frame += (streamObject->FramesPerBuffer * streamObject->BufferCount);

   if (transferObject->Frame < streamObject->NumPackets)
   {
      Ezusb_KdPrint(("IsoTransferComplete setting up the next transfer at frame %d\n",transferObject->Frame));

      //
      // reinitialize the URB for the next transfer.
      //
      urbSize = (USHORT)GET_ISO_URB_SIZE(streamObject->FramesPerBuffer);
      RtlZeroMemory(urb,urbSize);

      urb->UrbHeader.Length = urbSize;
      urb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
      urb->UrbIsochronousTransfer.PipeHandle = streamObject->PipeInfo->PipeHandle;
      urb->UrbIsochronousTransfer.TransferFlags =
         USB_ENDPOINT_DIRECTION_IN(streamObject->PipeInfo->EndpointAddress) ? USBD_TRANSFER_DIRECTION_IN : 0;
      urb->UrbIsochronousTransfer.TransferFlags |=
         USBD_START_ISO_TRANSFER_ASAP;
      urb->UrbIsochronousTransfer.TransferFlags |=
         USBD_SHORT_TRANSFER_OK;
      urb->UrbIsochronousTransfer.TransferBufferLength =
         streamObject->PacketSize * streamObject->FramesPerBuffer;
      urb->UrbIsochronousTransfer.TransferBuffer =
         ((PUCHAR) streamObject->TransferBuffer) +  (transferObject->Frame * streamObject->PacketSize);
      urb->UrbIsochronousTransfer.NumberOfPackets = streamObject->FramesPerBuffer;

      for (i=0; i<streamObject->FramesPerBuffer; i++)
      {
         urb->UrbIsochronousTransfer.IsoPacket[i].Offset = i * streamObject->PacketSize;
         urb->UrbIsochronousTransfer.IsoPacket[i].Length = streamObject->PacketSize;
      }

      //
      // initialize the IRP for the next transfer
      // cuz lynn says I hafta
      //
      IoInitializeIrp(Irp,
                     IoSizeOfIrp((pdx->StackDeviceObject->StackSize + 1)),
                     (CCHAR)(pdx->StackDeviceObject->StackSize + 1));

      nextStack = IoGetNextIrpStackLocation(Irp);

      nextStack->Parameters.Others.Argument1 = transferObject->Urb;
      nextStack->Parameters.DeviceIoControl.IoControlCode = 
         IOCTL_INTERNAL_USB_SUBMIT_URB;                    
      nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

      IoSetCompletionRoutine(Irp,
             IsoTransferComplete,
             transferObject,
             TRUE,
             TRUE,
             TRUE);

      //
      // submit the request
      //
      status = IoCallDriver(pdx->StackDeviceObject,Irp);
      Ezusb_KdPrint(("IsoTransferComplete Resubmit Irp Status 0x%x\n",status));
      ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
   }
   else
   {
      Ezusb_KdPrint(("IsoTransferComplete All done, setting event\n"));

      IoFreeIrp(Irp);
      ExFreePool(urb);

      KeSetEvent(&transferObject->Done,1,FALSE);

      ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
   }

   return ntStatus;
}

NTSTATUS Ezusb_StartIsoTransfer(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   PIO_STACK_LOCATION         irpStack = IoGetCurrentIrpStackLocation (Irp);
   PISO_TRANSFER_CONTROL      isoControl =
                              (PISO_TRANSFER_CONTROL)Irp->AssociatedIrp.SystemBuffer;
   ULONG                      bufferLength =
                              irpStack->Parameters.DeviceIoControl.OutputBufferLength;
   ULONG                      packetSize = 0;
   PUSBD_INTERFACE_INFORMATION interfaceInfo = NULL;
   PUSBD_PIPE_INFORMATION     pipeInfo = NULL;
   USBD_PIPE_HANDLE           pipeHandle = NULL;
   PISO_STREAM_OBJECT         streamObject;
   ULONG                      i;

   //
   // verify that the selected pipe is valid, and get a handle to it. If anything
   // is wrong, return an error
   //
   interfaceInfo = pdx->Interface;

   if (!interfaceInfo)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() no interface info - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }
   
   if (isoControl->PipeNum > interfaceInfo->NumberOfPipes)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeInfo = &(interfaceInfo->Pipes[isoControl->PipeNum]);

   if (!(pipeInfo->PipeType == UsbdPipeTypeIsochronous))
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeHandle = pipeInfo->PipeHandle;

   if (!pipeHandle)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }

   //
   // verify that the FramesPerBuffer and BufferCount parameters are correct.
   // A "feature" of this function is that PacketCount must
   // be zero modulus (FramesPerBuffer * BufferCount).
   //
   if (isoControl->PacketCount % (isoControl->FramesPerBuffer * isoControl->BufferCount))
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() Invalid parameter - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   //
   // verify that the caller has provided a buffer large enough for
   // the requested transfer.  The buffer must have room for all of
   // the ISO data plus room for an array of ISO Packet Descriptor
   // objects (1 for each packet transferred).
   //
   packetSize = isoControl->PacketSize;

   if (bufferLength < (isoControl->PacketCount * (packetSize + sizeof(USBD_ISO_PACKET_DESCRIPTOR))))
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() OutputBuffer too small - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }

   //
   // Allocate the streamObject
   //
   streamObject = ExAllocatePool(NonPagedPool, sizeof(ISO_STREAM_OBJECT));

   if (!streamObject)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate stream object - Exiting\n"));
      return STATUS_NO_MEMORY;
   }

   //
   // FramesPerBuffer specifies how many frames of ISO data are transferred
   // by a single URB.  This field corresponds to the NumberOfPackets
   // field in the ISO transfer URB (_URB_ISOCH_TRANSFER)
   //
   streamObject->FramesPerBuffer = isoControl->FramesPerBuffer;

   //
   // BufferCount specifies how many IRP's are queued to carry out an ISO transfer.
   //
   streamObject->BufferCount = isoControl->BufferCount;

   streamObject->DeviceObject = fdo;
   streamObject->PipeInfo = pipeInfo;
   streamObject->PacketSize = packetSize;
   streamObject->NumPackets = isoControl->PacketCount;
   streamObject->TransferBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
   streamObject->TransferBufferLength = streamObject->PacketSize * streamObject->NumPackets;
   streamObject->IsoDescriptorBuffer = (PUCHAR) streamObject->TransferBuffer + streamObject->TransferBufferLength;
   
   streamObject->TransferObject = ExAllocatePool(NonPagedPool,
                            sizeof(ISO_TRANSFER_OBJECT) * streamObject->BufferCount);

   if (!streamObject->TransferObject)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate transfer object - Exiting\n"));
      return STATUS_NO_MEMORY;
   }

   for (i=0; i < streamObject->BufferCount; i++)
   {
      InitTransferObject(streamObject,i);
   }

   for (i=0; i < streamObject->BufferCount; i++)
   {
      IoCallDriver(pdx->StackDeviceObject,
                  streamObject->TransferObject[i].Irp);
   }

   //
   // wait for those guys to finish
   //
   for (i=0; i < streamObject->BufferCount; i++)
   {
      KeWaitForSingleObject(
                    &streamObject->TransferObject[i].Done,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);
   }

   //
   // free the stream and transfer objects
   //
   ExFreePool(streamObject->TransferObject);
   ExFreePool(streamObject);

   return STATUS_SUCCESS;
}

NTSTATUS IsoStreamTransferComplete(
   IN PDEVICE_OBJECT bunkfdo,
   IN PIRP Irp,
   IN PVOID Context
   )
{
   NTSTATUS ntStatus,status;
   PISO_TRANSFER_OBJECT transferObject = (PISO_TRANSFER_OBJECT) Context;
   PISO_STREAM_OBJECT streamObject = transferObject->StreamObject;
   PDEVICE_OBJECT fdo = streamObject->DeviceObject;
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   PIO_STACK_LOCATION nextStack;
   PURB urb = transferObject->Urb;
   USHORT urbSize = 0;
   ULONG i;
   PUSBD_PIPE_INFORMATION     pipeInfo = streamObject->PipeInfo;

   Ezusb_KdPrint(("IsoTransferComplete Irp Status 0x%x\n",Irp->IoStatus.Status));
   for (i=0;i<streamObject->FramesPerBuffer; i++)
   {
      Ezusb_KdPrint (("Packet %d length = %d status = %d\n",i,
         urb->UrbIsochronousTransfer.IsoPacket[i].Length,urb->UrbIsochronousTransfer.IsoPacket[i].Status));
   }

   //
   // write the transfer descriptors to the descriptor ring buffer
   //
   WriteRingBuffer(streamObject->DescriptorRingBuffer,
                   (PUCHAR) &urb->UrbIsochronousTransfer.IsoPacket[0],
                   (streamObject->FramesPerBuffer * sizeof(USBD_ISO_PACKET_DESCRIPTOR)));

   //
   // write the transfer data to the data ring buffer
   //
   WriteRingBuffer(streamObject->DataRingBuffer,
                   urb->UrbIsochronousTransfer.TransferBuffer,
                   (streamObject->FramesPerBuffer * streamObject->PacketSize));


   //
   // If no errors occured, re-initialize and re-submit the IRP/URB
   //
	if (NT_SUCCESS(Irp->IoStatus.Status) && !pdx->StopIsoStream)
   {

      urbSize = (USHORT)GET_ISO_URB_SIZE(streamObject->FramesPerBuffer);
      RtlZeroMemory(urb,urbSize);

      urb->UrbHeader.Length = urbSize;
      urb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
      urb->UrbIsochronousTransfer.PipeHandle = streamObject->PipeInfo->PipeHandle;
      urb->UrbIsochronousTransfer.TransferFlags =
         USB_ENDPOINT_DIRECTION_IN(streamObject->PipeInfo->EndpointAddress) ? USBD_TRANSFER_DIRECTION_IN : 0;
      urb->UrbIsochronousTransfer.TransferFlags |=
         USBD_START_ISO_TRANSFER_ASAP;
      urb->UrbIsochronousTransfer.TransferFlags |=
         USBD_SHORT_TRANSFER_OK;
      urb->UrbIsochronousTransfer.TransferBufferLength =
         streamObject->PacketSize * streamObject->FramesPerBuffer;
      urb->UrbIsochronousTransfer.TransferBuffer =
         ((PUCHAR) streamObject->TransferBuffer) +  (transferObject->Frame * streamObject->PacketSize);
      urb->UrbIsochronousTransfer.NumberOfPackets = streamObject->FramesPerBuffer;

      for (i=0; i<streamObject->FramesPerBuffer; i++)
      {
         urb->UrbIsochronousTransfer.IsoPacket[i].Offset = i * streamObject->PacketSize;
         urb->UrbIsochronousTransfer.IsoPacket[i].Length = streamObject->PacketSize;
      }

      //
      // initialize the IRP for the next transfer
      // cuz lynn says I hafta
      //
      IoInitializeIrp(Irp,
                     IoSizeOfIrp((pdx->StackDeviceObject->StackSize + 1)),
                     (CCHAR)(pdx->StackDeviceObject->StackSize + 1));

      nextStack = IoGetNextIrpStackLocation(Irp);

      nextStack->Parameters.Others.Argument1 = transferObject->Urb;
      nextStack->Parameters.DeviceIoControl.IoControlCode = 
         IOCTL_INTERNAL_USB_SUBMIT_URB;                    
      nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

      IoSetCompletionRoutine(Irp,
             IsoStreamTransferComplete,
             transferObject,
             TRUE,
             TRUE,
             TRUE);

      //
      // submit the request
      //
      status = IoCallDriver(pdx->StackDeviceObject,Irp);
      //Ezusb_KdPrint(("IsoTransferComplete Resubmit Irp Status 0x%x\n",status));
      ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
   }
   else
   {
      Ezusb_KdPrint(("IsoTransferComplete All done, setting event\n"));

      IoFreeIrp(Irp);
      ExFreePool(urb);

      streamObject->PendingTransfers--;
      if (streamObject->PendingTransfers == 0)
      {
         ExFreePool(streamObject->TransferBuffer);
         ExFreePool(streamObject->TransferObject);
         FreeRingBuffer(streamObject->DataRingBuffer);
         pdx->DataRingBuffer = NULL;
         FreeRingBuffer(streamObject->DescriptorRingBuffer);
         pdx->DescriptorRingBuffer = NULL;

         ExFreePool(streamObject);
      }

      ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
   }

   return ntStatus;
}

NTSTATUS Ezusb_StartIsoStream(
   IN PDEVICE_OBJECT fdo,
   IN PIRP Irp
   )
{
   PDEVICE_EXTENSION          pdx = fdo->DeviceExtension;
   PIO_STACK_LOCATION         irpStack = IoGetCurrentIrpStackLocation (Irp);
   PISO_TRANSFER_CONTROL      isoControl =
                              (PISO_TRANSFER_CONTROL)Irp->AssociatedIrp.SystemBuffer;
   PUSBD_INTERFACE_INFORMATION interfaceInfo = NULL;
   PUSBD_PIPE_INFORMATION     pipeInfo = NULL;
   USBD_PIPE_HANDLE           pipeHandle = NULL;
   PISO_STREAM_OBJECT         streamObject;
   ULONG                      i;

   //
   // verify that the selected pipe is valid, and get a handle to it. If anything
   // is wrong, return an error
   //
   interfaceInfo = pdx->Interface;

   if (!interfaceInfo)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() no interface info - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }
   
   if (isoControl->PipeNum > interfaceInfo->NumberOfPipes)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeInfo = &(interfaceInfo->Pipes[isoControl->PipeNum]);

   if (!(pipeInfo->PipeType == UsbdPipeTypeIsochronous))
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_INVALID_PARAMETER;
   }

   pipeHandle = pipeInfo->PipeHandle;

   if (!pipeHandle)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() invalid pipe - Exiting\n"));
      return STATUS_UNSUCCESSFUL;
   }

   //
   // Allocate the streamObject
   //
   streamObject = ExAllocatePool(NonPagedPool, sizeof(ISO_STREAM_OBJECT));

   if (!streamObject)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate stream object - Exiting\n"));
      return STATUS_NO_MEMORY;
   }

   //
   // FramesPerBuffer specifies how many frames of ISO data are transferred
   // by a single URB.  This field corresponds to the NumberOfPackets
   // field in the ISO transfer URB (_URB_ISOCH_TRANSFER)
   //
// MDNISO   streamObject->PendingTransfers = 
   streamObject->FramesPerBuffer = isoControl->FramesPerBuffer;

   //
   // BufferCount specifies how many IRP's are queued to carry out an ISO transfer.
   //
   streamObject->PendingTransfers = 
   streamObject->BufferCount = isoControl->BufferCount;

   streamObject->DeviceObject = fdo;
   streamObject->PipeInfo = pipeInfo;
   streamObject->PacketSize = isoControl->PacketSize;
   streamObject->TransferBufferLength = streamObject->PacketSize * streamObject->FramesPerBuffer * streamObject->BufferCount;
   streamObject->TransferBuffer = ExAllocatePool(NonPagedPool, streamObject->TransferBufferLength);
   if (!streamObject->TransferBuffer)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate transfer buffer - Exiting\n"));
      return STATUS_NO_MEMORY;
   }

   streamObject->DataRingBuffer = AllocRingBuffer(isoControl->PacketCount * isoControl->PacketSize);
   if (!streamObject->DataRingBuffer)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate data ring buffer - Exiting\n"));
      return STATUS_NO_MEMORY;
   }
   pdx->DataRingBuffer = streamObject->DataRingBuffer;
   
   streamObject->DescriptorRingBuffer =
      AllocRingBuffer(isoControl->PacketCount * sizeof(USBD_ISO_PACKET_DESCRIPTOR));
   if (!streamObject->DescriptorRingBuffer)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate descriptor ring buffer - Exiting\n"));
      return STATUS_NO_MEMORY;
   }
   pdx->DescriptorRingBuffer = streamObject->DescriptorRingBuffer;

   streamObject->TransferObject = ExAllocatePool(NonPagedPool,
                            sizeof(ISO_TRANSFER_OBJECT) * streamObject->BufferCount);

   if (!streamObject->TransferObject)
   {
      Ezusb_KdPrint(("Ezusb_StartIsoTransfer() unable to allocate transfer object - Exiting\n"));
      return STATUS_NO_MEMORY;
   }

   for (i=0; i < streamObject->BufferCount; i++)
   {
      InitTransferObject(streamObject,i);

      //
      // override the completion routine that InitTransferObject registered
      //
      IoSetCompletionRoutine(streamObject->TransferObject[i].Irp,
                             IsoStreamTransferComplete,
                             &streamObject->TransferObject[i],
                             TRUE,
                             TRUE,
                             TRUE);


   }

   pdx->StopIsoStream = FALSE;

   for (i=0; i < streamObject->BufferCount; i++)
   {
      IoCallDriver(pdx->StackDeviceObject,
                  streamObject->TransferObject[i].Irp);
   }

   return STATUS_SUCCESS;
}

NTSTATUS InitTransferObject(
   IN OUT PISO_STREAM_OBJECT streamObject,
   IN ULONG index
   )
{
   PISO_TRANSFER_OBJECT transferObject = &streamObject->TransferObject[index];
   PUSBD_PIPE_INFORMATION pipeInfo = streamObject->PipeInfo;
   USHORT urbSize = 0;
   CCHAR stackSize;
   PIO_STACK_LOCATION nextStack = NULL;
   PURB urb = NULL;
   PIRP irp = NULL;
   PDEVICE_EXTENSION pdx =
      (PDEVICE_EXTENSION) streamObject->DeviceObject->DeviceExtension;
   ULONG i;


   //
   // allocate and prepare the URB
   //
   urbSize = (USHORT)GET_ISO_URB_SIZE(streamObject->FramesPerBuffer);

   urb = ExAllocatePool(NonPagedPool, urbSize);
   RtlZeroMemory(urb,urbSize);

   urb->UrbHeader.Length = urbSize;
   urb->UrbHeader.Function = URB_FUNCTION_ISOCH_TRANSFER;
   urb->UrbIsochronousTransfer.PipeHandle = pipeInfo->PipeHandle;
   urb->UrbIsochronousTransfer.TransferFlags =
      USB_ENDPOINT_DIRECTION_IN(pipeInfo->EndpointAddress) ? USBD_TRANSFER_DIRECTION_IN : 0;
   urb->UrbIsochronousTransfer.TransferFlags |=
      USBD_START_ISO_TRANSFER_ASAP;
   urb->UrbIsochronousTransfer.TransferFlags |=
      USBD_SHORT_TRANSFER_OK;
   urb->UrbIsochronousTransfer.TransferBufferLength =
      streamObject->PacketSize * streamObject->FramesPerBuffer;
   urb->UrbIsochronousTransfer.TransferBuffer =
      ((PUCHAR) streamObject->TransferBuffer) +  (index * streamObject->PacketSize * streamObject->FramesPerBuffer);

   urb->UrbIsochronousTransfer.NumberOfPackets = streamObject->FramesPerBuffer;

   //
   // setup the ISO packet descriptors
   //
   for (i=0; i<streamObject->FramesPerBuffer; i++)
   {
      urb->UrbIsochronousTransfer.IsoPacket[i].Offset = i * streamObject->PacketSize;
      urb->UrbIsochronousTransfer.IsoPacket[i].Length = streamObject->PacketSize;
   }

   stackSize = (CCHAR) (pdx->StackDeviceObject->StackSize + 1);

   //
   // allocate and prepare the IRP
   //
   irp = IoAllocateIrp(stackSize, FALSE);
   IoInitializeIrp(irp, irp->Size, stackSize);

   nextStack = IoGetNextIrpStackLocation(irp);

   nextStack->Parameters.Others.Argument1 = urb;
   nextStack->Parameters.DeviceIoControl.IoControlCode = 
      IOCTL_INTERNAL_USB_SUBMIT_URB;                    
   nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

   IoSetCompletionRoutine(irp,
                          IsoTransferComplete,
                          transferObject,
                          TRUE,
                          TRUE,
                          TRUE);

   transferObject->Frame = index * streamObject->FramesPerBuffer;
   transferObject->Urb = urb;
   transferObject->Irp = irp;
   transferObject->StreamObject = streamObject;
   KeInitializeEvent(&transferObject->Done, NotificationEvent, FALSE);

   return STATUS_SUCCESS;
}

NTSTATUS Ezusb_8051Reset(
   PDEVICE_OBJECT fdo,
   UCHAR resetBit
   )
/*++

Routine Description:
   Uses the ANCHOR LOAD vendor specific command to either set or release the
   8051 reset bit in the EZ-USB chip.

Arguments:
   fdo - pointer to the device object for this instance of an Ezusb Device
   resetBit - 1 sets the 8051 reset bit (holds the 8051 in reset)
              0 clears the 8051 reset bit (8051 starts running)
              
Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS ntStatus;
   PURB urb = NULL;

   urb = ExAllocatePool(NonPagedPool, 
                       sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

   if (urb)
   {

      // toggle the EZ-USB reset bit (harmless on FX2)
      RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

      urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

      urb->UrbControlVendorClassRequest.TransferBufferLength = 1;
      urb->UrbControlVendorClassRequest.TransferBuffer = &resetBit;
      urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
      urb->UrbControlVendorClassRequest.Request = ANCHOR_LOAD_INTERNAL;
      urb->UrbControlVendorClassRequest.Value = CPUCS_REG_EZUSB;
      urb->UrbControlVendorClassRequest.Index = 0;

      ntStatus = Ezusb_CallUSBD(fdo, urb);

      // toggle the FX2 reset bit (harmless on EZ-USB)
      RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

      urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

      urb->UrbControlVendorClassRequest.TransferBufferLength = 1;
      urb->UrbControlVendorClassRequest.TransferBuffer = &resetBit;
      urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
      urb->UrbControlVendorClassRequest.Request = ANCHOR_LOAD_INTERNAL;
      urb->UrbControlVendorClassRequest.Value = CPUCS_REG_FX2;
      urb->UrbControlVendorClassRequest.Index = 0;

      ntStatus = Ezusb_CallUSBD(fdo, urb);
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;
   }

   if (urb)
      ExFreePool(urb);

   return ntStatus;
}

//
// this is the number of bytes of firmware to download per setup transfer.
// 
#define CHUNK_SIZE 64

NTSTATUS Ezusb_AnchorDownload(
   PDEVICE_OBJECT fdo,
   WORD offset,
   PUCHAR downloadBuffer,
   ULONG downloadSize
   )
/*++

Routine Description:
   Uses the ANCHOR LOAD vendor specific command to download code to the EZ-USB
   device.  The actual code is stored as data within the driver binary in the
   global 'firmware' which is an EZUSB_FIRMWARE struct included in the file
   firmware.c.

Arguments:
   fdo - pointer to the device object for this instance of an Ezusb Device
   downloadBuffer - pointer to the firmware image
   downloadSize - total size (bytes) of the firmware image to download
   
Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS ntStatus;
   PURB urb = NULL;
   int i;
   int chunkCount;
   PUCHAR ptr = downloadBuffer;

   urb = ExAllocatePool(NonPagedPool, 
                       sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

   if (urb)
   {
      chunkCount = ((downloadSize + CHUNK_SIZE - 1) / CHUNK_SIZE);
      //
      // The download will be split into CHUNK_SIZE pieces and
      // downloaded with multiple setup transfers.  For the Rev B parts
      // CHUNK_SIZE should not exceed 64 bytes, as larger transfers can
      // result in data corruption when other USB devices are present.
      //
      for (i = 0; i < chunkCount; i++)
      {
         RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

         urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
         urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

         urb->UrbControlVendorClassRequest.TransferBufferLength =
            ((i == (chunkCount - 1)) &&  (downloadSize % CHUNK_SIZE)) ?
            (downloadSize % CHUNK_SIZE) :
            CHUNK_SIZE;

         urb->UrbControlVendorClassRequest.TransferBuffer = ptr;
         urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
         urb->UrbControlVendorClassRequest.Request = ANCHOR_LOAD_INTERNAL;
         urb->UrbControlVendorClassRequest.Value = (i * CHUNK_SIZE) + offset;
         urb->UrbControlVendorClassRequest.Index = 0;

         ntStatus = Ezusb_CallUSBD(fdo, urb);

         if (!NT_SUCCESS(ntStatus))
            break;

         ptr += CHUNK_SIZE;
      }
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;
   }

   if (urb)
      ExFreePool(urb);

   return ntStatus;
}

NTSTATUS Ezusb_DownloadIntelHex(
   PDEVICE_OBJECT fdo,
   PINTEL_HEX_RECORD hexRecord
   )
/*++

Routine Description:
   This function downloads Intel Hex Records to the EZ-USB device.  If any of the hex records
   are destined for external RAM, then the caller must have previously downloaded firmware
   to the device that knows how to download to external RAM (ie. firmware that implements
   the ANCHOR_LOAD_EXTERNAL vendor specific command).

Arguments:
   fdo - pointer to the device object for this instance of an Ezusb Device
   hexRecord - pointer to an array of INTEL_HEX_RECORD structures.  This array
               is terminated by an Intel Hex End record (Type = 1).

Return Value:
   STATUS_SUCCESS if successful,
   STATUS_UNSUCCESSFUL otherwise

--*/
{
   NTSTATUS ntStatus;
   PURB urb = NULL;
   PINTEL_HEX_RECORD ptr = hexRecord;

   urb = ExAllocatePool(NonPagedPool, 
                       sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

   if (urb)
   {
      //
      // The download must be performed in two passes.  The first pass loads all of the
      // external addresses, and the 2nd pass loads to all of the internal addresses.
      // why?  because downloading to the internal addresses will probably wipe out the firmware
      // running on the device that knows how to receive external ram downloads.
      //
      //
      // First download all the records that go in external ram
      //
      while (ptr->Type == 0)
      {
         if (!INTERNAL_RAM(ptr->Address))
         {
            RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
            urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;
            urb->UrbControlVendorClassRequest.TransferBufferLength = ptr->Length;
            urb->UrbControlVendorClassRequest.TransferBuffer = ptr->Data;
            urb->UrbControlVendorClassRequest.Request = ANCHOR_LOAD_EXTERNAL;
            urb->UrbControlVendorClassRequest.Value = ptr->Address;
            urb->UrbControlVendorClassRequest.Index = 0;

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            if (!NT_SUCCESS(ntStatus))
               break;
         }
         ptr++;
      }

      //
      // Now download all of the records that are in internal RAM.  Before starting
      // the download, stop the 8051.
      //
      Ezusb_8051Reset(fdo,1);
      ptr = hexRecord;
      while (ptr->Type == 0)
      {
         if (INTERNAL_RAM(ptr->Address))
         {
            RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

            urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
            urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;
            urb->UrbControlVendorClassRequest.TransferBufferLength = ptr->Length;
            urb->UrbControlVendorClassRequest.TransferBuffer = ptr->Data;
            urb->UrbControlVendorClassRequest.Request = ANCHOR_LOAD_INTERNAL;
            urb->UrbControlVendorClassRequest.Value = ptr->Address;
            urb->UrbControlVendorClassRequest.Index = 0;

            ntStatus = Ezusb_CallUSBD(fdo, urb);

            if (!NT_SUCCESS(ntStatus))
               break;
         }
         ptr++;
      }

   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;
   }

   if (urb)
      ExFreePool(urb);

   return ntStatus;
}

PRING_BUFFER
AllocRingBuffer(
   ULONG    Size
   )
{
   PRING_BUFFER   ringBuffer = NULL;

   ringBuffer = ExAllocatePool(NonPagedPool, sizeof(RING_BUFFER));

   if (!ringBuffer)
      return NULL;

   ringBuffer->buffer = ExAllocatePool(NonPagedPool, Size);

   if (!ringBuffer->buffer)
   {
      ExFreePool(ringBuffer);
      return NULL;
   }

   ringBuffer->inPtr = ringBuffer->buffer;
   ringBuffer->outPtr = ringBuffer->buffer;
   ringBuffer->totalSize = Size;
   ringBuffer->currentSize = 0;

   KeInitializeSpinLock(&ringBuffer->spinLock);

   return ringBuffer;
}

VOID
FreeRingBuffer(
   PRING_BUFFER   ringBuffer
   )
{
   ExFreePool(ringBuffer->buffer);
   ExFreePool(ringBuffer);
}

ULONG
ReadRingBuffer(
   PRING_BUFFER   ringBuffer,
   PUCHAR         readBuffer,
   ULONG          numberOfBytesToRead
   )
/*
   Routine Description:
   This routine reads data from a ring buffer.

   Arguments:
   ringBuffer - pointer to a ring buffer structure
   readBuffer - pointer to a user supplied buffer to transfer data into
   numberOfBytesToRead - number of bytes to read from the ring buffer

   Return Value:
   ULONG - number of bytes read.  May be smaller than requested number of bytes.
*/
{
   ULONG    byteCount;
   KIRQL    oldIrql;

   Ezusb_KdPrint(( "ReadRingBuffer() enter\n"));

   if (numberOfBytesToRead > ringBuffer->totalSize)
      return 0;

   if (ringBuffer->currentSize == 0)
      return 0;

   if ( numberOfBytesToRead > ringBuffer->currentSize )
      byteCount = ringBuffer->currentSize;
   else
      byteCount = numberOfBytesToRead;

   //
   // two cases.  Read either wraps or it doesn't.
   // Handle the non-wrapped case first
   //
   if ((ringBuffer->outPtr + byteCount - 1) < 
       (ringBuffer->buffer + ringBuffer->totalSize))
   {
      Ezusb_KdPrint(( "ReadRingBuffer() about to copy a\n"));
      RtlCopyMemory(readBuffer, ringBuffer->outPtr, byteCount);
      ringBuffer->outPtr += byteCount;
      if (ringBuffer->outPtr == ringBuffer->buffer + ringBuffer->totalSize)
         ringBuffer->outPtr = ringBuffer->buffer;
   }
   // now handle the wrapped case
   else
   {
      ULONG    fragSize;

      Ezusb_KdPrint(( "ReadRingBuffer() about to copy b\n"));
      // get the first half of the read
      fragSize = ringBuffer->buffer + ringBuffer->totalSize - ringBuffer->outPtr;
      RtlCopyMemory(readBuffer, ringBuffer->outPtr, fragSize);

      // now get the rest
      RtlCopyMemory(readBuffer + fragSize, ringBuffer->buffer, byteCount - fragSize);

      ringBuffer->outPtr = ringBuffer->buffer + byteCount - fragSize;
   }
 
   // 
   // update the current size of the ring buffer.  Use spinlock to insure
   // atomic operation.
   //
   KeAcquireSpinLock(&ringBuffer->spinLock, &oldIrql);
   ringBuffer->currentSize -= byteCount;
   KeReleaseSpinLock(&ringBuffer->spinLock, oldIrql);

   Ezusb_KdPrint(( "ReadRingBuffer() exit\n"));

   return byteCount;
}

ULONG
WriteRingBuffer(
   PRING_BUFFER   ringBuffer,
   PUCHAR         writeBuffer,
   ULONG          numberOfBytesToWrite
   )
/*
   Routine Description:
   This routine writes data to a ring buffer.  If the requested write size exceeds
   available space in the ring buffer, then the write is rejected.

   Arguments:
   ringBuffer - pointer to a ring buffer structure
   readBuffer - pointer to a user supplied buffer of data to copy to the ring buffer
   numberOfBytesToRead - number of bytes to write to the ring buffer

   Return Value:
   ULONG - number of bytes written.
*/
{
   ULONG    byteCount;
   KIRQL    oldIrql;

   if ( numberOfBytesToWrite >
        (ringBuffer->totalSize - ringBuffer->currentSize) )
   {
      Ezusb_KdPrint(( "WriteRingBuffer() OVERFLOW\n"));
      return 0;
   }

   byteCount = numberOfBytesToWrite;

   //
   // two cases.  Write either wraps or it doesn't.
   // Handle the non-wrapped case first
   //
   if ((ringBuffer->inPtr + byteCount - 1) < 
       (ringBuffer->buffer + ringBuffer->totalSize))
   {
      RtlCopyMemory(ringBuffer->inPtr, writeBuffer, byteCount);
      ringBuffer->inPtr += byteCount;
      if (ringBuffer->inPtr == ringBuffer->buffer + ringBuffer->totalSize)
         ringBuffer->inPtr = ringBuffer->buffer;
   }
   // now handle the wrapped case
   else
   {
      ULONG    fragSize;

      // write the first fragment
      fragSize = ringBuffer->buffer + ringBuffer->totalSize - ringBuffer->inPtr;
      RtlCopyMemory(ringBuffer->inPtr, writeBuffer, fragSize);

      // now write the rest
      RtlCopyMemory(ringBuffer->buffer, writeBuffer + fragSize, byteCount - fragSize);

      ringBuffer->inPtr = ringBuffer->buffer + byteCount - fragSize;
   }

   // 
   // update the current size of the ring buffer.  Use spinlock to insure
   // atomic operation.
   //
   KeAcquireSpinLock(&ringBuffer->spinLock, &oldIrql);
   ringBuffer->currentSize += byteCount;
   KeReleaseSpinLock(&ringBuffer->spinLock, oldIrql);

   return byteCount;
}

BOOLEAN IsFx2(
   IN  PDEVICE_OBJECT fdo
   )
/*
   Routine Description:
   This routine queries the device via a SETUP command to determine if it is
   an FX2 chip.  In order for this to work, the device must implement the vendor
   specific IN SETUP command 0xAC, which returns 1 if the chip is FX2 and 0
   otherwise.

   Arguments:
   fdo - our device object

   Return Value:
   BOOL - TRUE if FX2, FALSE otherwise
*/
{
   NTSTATUS ntStatus;
   PURB urb = NULL;
   UCHAR transferBuffer[64];

   urb = ExAllocatePool(NonPagedPool, 
                       sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

   if (urb)
   {
      RtlZeroMemory(urb,sizeof(struct  _URB_CONTROL_VENDOR_OR_CLASS_REQUEST));

      urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;

      urb->UrbControlVendorClassRequest.TransferFlags = USBD_TRANSFER_DIRECTION_IN ;
      urb->UrbControlVendorClassRequest.TransferBufferLength = 1;
      urb->UrbControlVendorClassRequest.TransferBuffer = transferBuffer;
      urb->UrbControlVendorClassRequest.TransferBufferMDL = NULL;
      urb->UrbControlVendorClassRequest.Request = ANCHOR_ISFX2;
      urb->UrbControlVendorClassRequest.Value = 0;
      urb->UrbControlVendorClassRequest.Index = 0;

      Ezusb_KdPrint (("**** About to query ISFX2\n"));

      ntStatus = Ezusb_CallUSBD(fdo, urb);
   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;
   }

   if (urb)
      ExFreePool(urb);

   if (transferBuffer[0] == 1)
   {
      return TRUE;
   }
   else
   {
      return FALSE;
   }
}

NTSTATUS Ezusb_SetFeature(
   IN PDEVICE_OBJECT fdo,
   IN PSET_FEATURE_CONTROL setFeatureControl
   )
/*
   Routine Description:
   This routine performs a Set Feature control transfer

   Arguments:
   fdo - our device object
   setFeatureControl - a data structure that contains the arguments for the
   set featire command

   Return Value:
   NTSTATUS 
*/
{
   NTSTATUS            ntStatus        = STATUS_SUCCESS;
   PURB                urb             = NULL;
   ULONG               length          = 0;
   PDEVICE_EXTENSION   pdx = fdo->DeviceExtension;

   Ezusb_KdPrint (("Enter Ezusb_SetFeature\n"));    

   urb = ExAllocatePool(NonPagedPool, 
                      sizeof(struct _URB_CONTROL_FEATURE_REQUEST));
                   
   if (urb)
   {
      RtlZeroMemory(urb,sizeof(struct _URB_CONTROL_FEATURE_REQUEST));

      urb->UrbHeader.Length = sizeof(struct _URB_CONTROL_FEATURE_REQUEST);
      urb->UrbHeader.Function = URB_FUNCTION_SET_FEATURE_TO_DEVICE;

      urb->UrbControlFeatureRequest.FeatureSelector = setFeatureControl->FeatureSelector;
      urb->UrbControlFeatureRequest.Index = setFeatureControl->Index;
                                                                  
      ntStatus = Ezusb_CallUSBD(fdo, urb);

   }
   else
   {
      ntStatus = STATUS_NO_MEMORY;        
   }        

   Ezusb_KdPrint (("Leaving Ezusb_SetFeature\n"));    

   return ntStatus;
}


 在代码中有涉到内存分配的有以下几个方面:

1、URB块
2、传送Buffer
3、获得接口Interface
4、获得配置描述符Descriptor
5、Vender请求
6、ISO_STREAM_OBJECT传送对象

我们都是采用ExAllocatePool来分配内存,关于驱动中内存分配的方法不止这几种,

大家可以参考我的另一篇文章:<<Window XP驱动开发(十九)Window驱动的内存管理>>

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Window XP驱动开发(十三) 芯片功能驱动端 (代码实现,针对USB2.0 芯片CY7C68013A) 的相关文章

随机推荐

  • ValueError: embedded null character

    问题 报错 ValueError embedded null character 原因 路径中包含 0 被认为是转义字符 不会自动识别为路径 导致报错 需注意含 0 t 等特殊转义字符的路径 解决方案 路径改为 1 D 00pythonPr
  • 电源滤波电路的设计

    一 电源滤波技术 常用的滤波措施有 去耦电容 电感 磁珠等 常用的滤波场景有 电源滤波 接口滤波等 在进行PCB设计时 滤波器件的摆放位置相当关键 对于电容类去耦的滤波原则是靠近滤波区域位置放置最佳 在滤波电路的EMC设计中 滤波的作用基本
  • 电磁场仿真工具们

    首先说明两点 1 不同的商用电磁场仿真工具可能采用的算法不同 但对于偏上电感等常见结构 在各仿真器设置正确的情况下 各仿真工具得到的结果应该相似 如果对仿真结果有疑问 最简单的验证方法是 对同一模型使用不同原理的两种电磁场仿真工具进行仿真
  • vue2使用 tinymce富文本编辑器-图片上传、粘贴图片上传致服务器

    1 安装tinymce富文本编辑器插件 npm i tinymce npm i tinymce tinymce vue 2 创建Editor js文件 封装组件 以便使用
  • c1048: [编程入门]自定义函数之字符串拷贝

    题目描述 有一字符串 包含n个字符 写一函数 将此字符串中从第m个字符开始的全部字符复制成为另一个字符串 输入 数字n 一行字符串 数字m 输出 从m开始的子串 样例输入复制 6 abcdef 3 样例输出复制 cdef 思路 两种方法 一
  • Kafka : KafkaProducer Closing the kafka producer with timeoutMillis

    1 美图 2 背景 一段kafka写入程序 不晓得为啥突然发现很多奇怪的日志 kafka 多线程发送数据 然后在本地是可以的 在服务器上是偶现的 我写了一个本地程序多线程生产数据 发现是没有问题的 Test public void mult
  • mysql“Access denied for user 'root'@'IP地址

    mysql Access denied for user root IP地址 2013 12 27 17 29 1308人阅读 评论 0 收藏 举报 分类 面试 经历 42 版权声明 本文为博主原创文章 未经博主允许不得转载 xshell连
  • kvm环境创建虚拟机

    使用libvirt的xml配置文件创建虚拟机 kvm支持的镜像很多 常用的是原始镜像 img 还有支持动态大小扩张的qocw2格式 首选 更优的选择是系统盘如C盘用img格式 数据盘用qcow2格式以减少服务器磁盘闲置空间 本文仅记录如何用
  • 微信H5如何关闭浏览器(如何监听手机的物理返回键)

    一 背景 背景是这样的 该项目进入h5时会通过 location replace xxx 或 location href xxx 跳转到某个地址 该地址会请求获得微信 openId 获取成功后再重定向到h5首页 那么问题来了 重定向会在微信
  • 《影响力》-社会心理学入门书

    这本书评价如何 豆瓣评分8 6 大多数人认可度还是很高的 查理芒格 穷查理宝典 的作者 觉得作者在 影响力 主题上 远超其他科学家 因此赠送了一股伯克希尔的A级股票 据说价值22w美元 财富 杂志推荐75本商业必读之一 我个人觉得 这本书其
  • 使用R语言中的survival包进行生存分析是一种常见的统计方法

    使用R语言中的survival包进行生存分析是一种常见的统计方法 在生存分析中 我们经常需要创建一个生存对象来存储事件发生时间和事件状态 在本文中 我们将介绍如何使用survival包中的Surv函数来创建生存对象 并解读其结果 Surv函
  • 代码随想录算法训练营第十九天

    动态规划系列5 6 7 8 377 组合总和 未看解答自己编写的青春版 重点 代码随想录的代码 我的代码 当天晚上理解后自己编写 求排列数的题 用二维DP过不了 自己捋逻辑的话 也是可以觉得有漏洞 但是怎么修改 一下子还没思路 包括后面的
  • GPRS公网通讯

    GPRS应用中TCP IP PPP基本概念 的原理与此差不多 实际上GPRS DTU上实现的是协议栈是TCP IP Over PPP 我们在使用时必须要有一些相应的概念 因此在下面我们就与SARO 3130P使用相关的一些事项作一些简要的说
  • gerrit提交出现remote rejected change closed错误

    看了这边文章 原因就像提示信息所说3203已经关闭了 change id找不到了 分析了原因 我的问题是因为之前的一次提交 3203 anandoned掉了 所以找不到这个change id 将这次提交重新恢复后 push成功
  • 区块链技术实战学习路线图

    请大家前往深入浅出区块链主站 获取最新内容 本章的文章越来越多 本文是一个索引帖 方便找到自己感兴趣的文章 你也可以使用左侧的分类 标签及搜索功能 有新文章时会更新本文 建议大家加入收藏夹中 如果你觉得本站不错 欢迎你转发给朋友 引言 给迷
  • 编写简单的linux shell脚本

    1 touch hello sh 2 vi hello sh 键入i 插入 bin sh echo hello world 键入 esc wq 3 chmod 700 hello sh 4 执行 hello sh
  • linux 上生成图片的问题 (awt)

    1 启动xwindow 执行命令 xhost local 2 参考下面资料 原因 Linux无图形支持环境配置 解决 在catalina sh 中加入 Djava awt headless true 或者在 bash profile中增加
  • Redis持久化AOF

    目录 1 AOF简介 2 AOF持久化流程 3 AOF默认不开启 4 AOF和RDB同时开启 redis听谁的 5 AOF启动 修复 恢复 6 AOF同步频率设置 7 Rewrite压缩 7 1 是什么 7 2 重写原理 如何实现重写 8
  • 微信小程序:自动生成打卡海报

    文章目录 1 前言 2 界面展示 3 部分代码展示以及原理解释 4 结语 完整项目下载 下载链接 1 前言 在当前的背单词小程序开发中 为满足用户学习完成后的展示需求 计划引入自动生成打卡海报功能 以提升用户参与度与推广效果 除了基本的海报
  • Window XP驱动开发(十三) 芯片功能驱动端 (代码实现,针对USB2.0 芯片CY7C68013A)

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要源码的可以与我联系 针对USB2 0 芯片CY7C68013A FPGA实现的高速传输应用来写XP下的USB驱动程序 说