www.pudn.com > wdm D12µÄÀý×Ó.rar > Ioctl.c


/*++ 
 
Copyright (c) 1995  Microsoft Corporation 
 
Module Name: 
 
   ioctl.c 
 
Abstract: 
 
    USB device driver for Philips D12 USB test board 
 
Environment: 
 
    kernel mode only 
 
Notes: 
 
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR 
  PURPOSE. 
 
  Copyright (c) 1996 Microsoft Corporation.  All Rights Reserved. 
 
 
Revision History: 
 
    5-4-96 : created 
 
--*/ 
 
#define DRIVER 
 
#include "wdm.h" 
#include "stdarg.h" 
#include "stdio.h" 
 
#include "usbdi.h" 
#include "usbdlib.h" 
#include "D12.h" 
 
#include "ioctl.h" 
#include "usbdlib.h" 
 
 
PUSB_CONFIGURATION_DESCRIPTOR 
D12_GetConfigDescriptor( 
    IN PDEVICE_OBJECT DeviceObject 
    ) 
/*++ 
 
Routine Description: 
 
Arguments: 
 
    DeviceObject - pointer to the device object for this instance of the 82930 
                    devcice. 
 
 
Return Value: 
 
    NT status code 
 
--*/ 
{ 
    PDEVICE_EXTENSION deviceExtension; 
    NTSTATUS ntStatus; 
    PURB urb; 
    ULONG siz; 
    PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL; 
 
    D12_KdPrint (("D12TEST.SYS: enter D12_GetConfigDescriptor\n")); 
 
    deviceExtension = DeviceObject->DeviceExtension; 
 
    urb = ExAllocatePool(NonPagedPool, 
                         sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); 
 
    if (urb) { 
 
        // BUGBUG 82930 chokes if on the next command if you don't get 
        // the entire descriptor on the first try 
 
        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 = D12_CallUSBD(DeviceObject, urb); 
 
            D12_KdPrint (("D12TEST.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; 
    } 
 
    D12_KdPrint (("D12TEST.SYS: exit D12_GetConfigDescriptor\n")); 
 
    return configurationDescriptor; 
} 
 
 
 
 
NTSTATUS 
D12_GetPortStatus( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PULONG PortStatus 
    ) 
/*++ 
 
Routine Description: 
 
    returns the port status for our device 
 
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 deviceExtension; 
 
    D12_KdPrint (("D12TEST.SYS: enter D12_GetPortStatus\n")); 
 
    deviceExtension = DeviceObject->DeviceExtension; 
 
    *PortStatus = 0; 
 
    // 
    // issue a synchronous request 
    // 
 
    KeInitializeEvent(&event, NotificationEvent, FALSE); 
 
    irp = IoBuildDeviceIoControlRequest( 
                IOCTL_INTERNAL_USB_GET_PORT_STATUS, 
                deviceExtension->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); 
 
    nextStack->Parameters.Others.Argument1 = PortStatus; 
 
    D12_KdPrint (("D12TEST.SYS: calling USBD port status api\n")); 
 
    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, 
                            irp); 
 
    D12_KdPrint (("D12TEST.SYS: return from IoCallDriver USBD %x\n", ntStatus)); 
 
    if (ntStatus == STATUS_PENDING) { 
 
        D12_KdPrint (("D12TEST.SYS: Wait for single object\n")); 
 
        status = KeWaitForSingleObject( 
                       &event, 
                       Suspended, 
                       KernelMode, 
                       FALSE, 
                       NULL); 
 
        D12_KdPrint (("D12TEST.SYS: Wait for single object, returned %x\n", status)); 
         
    } else { 
        ioStatus.Status = ntStatus; 
    } 
 
    D12_KdPrint (("D12TEST.SYS: Port status = %x\n", *PortStatus)); 
 
    // 
    // USBD maps the error code for us 
    // 
    ntStatus = ioStatus.Status; 
 
    D12_KdPrint (("D12TEST.SYS: D12_GetPortStatus (%x)\n", ntStatus)); 
 
    return ntStatus; 
} 
 
 
NTSTATUS 
D12_ResetParentPort( 
    IN IN PDEVICE_OBJECT DeviceObject 
    ) 
/*++ 
 
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 deviceExtension; 
 
    D12_KdPrint (("D12TEST.SYS: enter D12_ResetPort\n")); 
 
    deviceExtension = DeviceObject->DeviceExtension; 
 
    // 
    // issue a synchronous request 
    // 
 
    KeInitializeEvent(&event, NotificationEvent, FALSE); 
 
    irp = IoBuildDeviceIoControlRequest( 
                IOCTL_INTERNAL_USB_RESET_PORT, 
                deviceExtension->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); 
 
    D12_KdPrint (("D12TEST.SYS: calling USBD enable port api\n")); 
 
    ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, 
                            irp); 
                             
    D12_KdPrint (("D12TEST.SYS: return from IoCallDriver USBD %x\n", ntStatus)); 
 
    if (ntStatus == STATUS_PENDING) { 
 
        D12_KdPrint (("D12TEST.SYS: Wait for single object\n")); 
 
        status = KeWaitForSingleObject( 
                       &event, 
                       Suspended, 
                       KernelMode, 
                       FALSE, 
                       NULL); 
 
        D12_KdPrint (("D12TEST.SYS: Wait for single object, returned %x\n", status)); 
         
    } else { 
        ioStatus.Status = ntStatus; 
    } 
 
    // 
    // USBD maps the error code for us 
    // 
    ntStatus = ioStatus.Status; 
 
    D12_KdPrint (("D12TEST.SYS: D12_ResetPort (%x)\n", ntStatus)); 
 
    return ntStatus; 
} 
 
 
NTSTATUS 
D12_ReadWriteRegister( 
    IN  PDEVICE_OBJECT DeviceObject, 
    IN  PIRP Irp, 
    IN  BOOLEAN  bWrite 
    ) 
/*++ 
Routine Description: 
    This function is called for IOCTLs to Read or Write. 
    For WRITEs, the data is retrieved from the SystemBuffer and sent to the device. 
     
Arguments: 
    DeviceObject - pointer to the device object for this instance of the D12 device. 
    Irp          - pointer to IRP 
    Read         - if TRUE this is a Device-to-Host (Read from device) transfer 
                   if FALSE this is a Host-to-Device (Write to device) transfer 
 
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 
     
--*/ 
{ 
    USBD_INTERFACE_INFORMATION *    pInterfaceInfo; 
    PIO_STACK_LOCATION              irpStack; 
    PDEVICE_EXTENSION               deviceExtension;      
    NTSTATUS                        ntStatus; 
    PVOID                           ioBuffer; 
    ULONG                           length; 
    ULONG                           inputBufferLength; 
    ULONG                           outputBufferLength; 
    ULONG                           siz; 
    PURB                            urb; 
    USHORT							wValue, wIndex; 
	PIO_BLOCK						ioBlock; 
	 
	D12_KdPrint(("D12TEST.SYS: enter D12_ReadWriteRegister\n")); 
 
    irpStack = IoGetCurrentIrpStackLocation (Irp); 
    ASSERT (irpStack != NULL); 
     
    deviceExtension = DeviceObject->DeviceExtension; 
    ASSERT (deviceExtension != NULL); 
 
    ioBuffer			= Irp->AssociatedIrp.SystemBuffer; 
	ioBlock				= (PIO_BLOCK) ioBuffer; 
    inputBufferLength	= irpStack->Parameters.DeviceIoControl.InputBufferLength; 
    outputBufferLength	= irpStack->Parameters.DeviceIoControl.OutputBufferLength; 
 
    //DEBUG ONLY 
    if ((ioBuffer == NULL) || (inputBufferLength == 0) ){ 
        D12_KdPrint (("D12TEST.SYS: ERROR! ioBuffer %X | inBufLen: %d | outBufLen %d\n", 
                          ioBuffer, inputBufferLength, outputBufferLength)); 
         
        Irp->IoStatus.Information = 0; 
        return (STATUS_NO_MEMORY); 
         
    } //DEBUG ONLY 
         
                           
    siz = sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST); 
 
	// allocate urb 
	urb = ExAllocatePool(NonPagedPool, siz); 
 
    // By convention, the first byte of the buffer is reserved for the ReportID, so 
    // skip over the first Byte when specifying the transferbuffer 
	 
	length = ioBlock->uLength; 
	wValue = ioBlock->uOffset; 
	wIndex = ioBlock->uIndex; 
 
    D12_KdPrint (("D12TEST.SYS: ReadWriteRegister: ioBuffer = %p, wIndex = %x, wLength = %lx, wValue = %x.\n", 
		ioBlock->pbyData, wIndex, length, wValue)); 
 
    if (bWrite) { 
        // A WRITE operation implies that the data is gotten from the User's Input Data Buffer 
   		UsbBuildVendorRequest(urb,				//ptr to urb 
						URB_FUNCTION_VENDOR_DEVICE, 
						(USHORT) siz,			//siz of urb 
						0,					 
						0x0,					//reservedbits=bmRequestType 
						0x0C,					//request = USBSCAN IOCTL_WRITE_REGISTER 
						wValue,					 
						wIndex,				 
						ioBlock->pbyData,			//TransferBuffer 
						NULL,					//mdl (unused) 
						length,					//bufferlength 
					    NULL);					//link 
    } else { 
   		// A READ operation implies that the data is placed in the User's Output Data Buffer 
		UsbBuildVendorRequest(urb,				//ptr to urb 
						URB_FUNCTION_VENDOR_DEVICE, 
						(USHORT) siz,			//siz of urb 
						USBD_TRANSFER_DIRECTION_IN, 
						0x0,					//reservedbits=bmRequestType 
						0x0C,					//request = USBSCAN IOCTL_READ_REGISTER 
						wValue,					 
						wIndex,				 
						ioBuffer,				//TransferBuffer 
						NULL,					//mdl (unused) 
						length,					//bufferlength 
					    NULL);					//link 
 }/* else */ 
	/* 
    /* 
	// Call the USB Stack. This call blocks until the device returns data.  Note that if the 
	// device NAKs forever, this call will not return and this thread will hang.  To correct  
	// this, a timeout value can be specified and a Kernel-Mode timer used to wake  
	// up the driver and then cancel this transfer.  Future versions of this sample  
	// driver will demonstrate that capability, or consult the Windows NT DDK for  
	// further details on the Kernel Timer functionality as well as the  
	// IoCancelIrp functionality. 
	*/ 
	ntStatus = D12_CallUSBD(DeviceObject, urb); 
 
	// The Information field tells IOM how much to copy back into the 
    // usermode buffer in the BUFFERED method 
    if (NT_SUCCESS(ntStatus) && bWrite) { 
        Irp->IoStatus.Information =length;	//sizeof (ULONG); 
    
    }else if (NT_SUCCESS(ntStatus)) { 
 
        D12_KdPrint (("D12TEST.SYS: Sucessfully Transferred %d Bytes\n", urb->UrbControlVendorClassRequest.TransferBufferLength)); 
 
        Irp->IoStatus.Information = (urb->UrbControlVendorClassRequest.TransferBufferLength);	// + sizeof(UCHAR)); 
    
    } 
     
	Irp->IoStatus.Status = ntStatus; 
 
	// free allocated urb 
	ExFreePool(urb); 
 
	D12_KdPrint(("D12TEST.SYS: exit D12_ReadWriteRegister\n")); 
 
    return (ntStatus); 
} 
 
 
NTSTATUS 
D12_ProcessIOCTL( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp 
    ) 
/*++ 
 
Routine Description: 
 
Arguments: 
 
    DeviceObject - pointer to the device object for this instance of the 82930 
                    devcice. 
 
 
Return Value: 
 
    NT status code 
 
--*/ 
{ 
    PIO_STACK_LOCATION irpStack; 
    PVOID ioBuffer; 
    ULONG inputBufferLength; 
    ULONG outputBufferLength; 
    PDEVICE_EXTENSION deviceExtension; 
    ULONG ioControlCode; 
    NTSTATUS ntStatus; 
    ULONG length; 
    PUCHAR pch; 
    PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor; 
    PUSB_DEVICE_DESCRIPTOR deviceDescriptor; 
 
    D12_KdPrint (("D12TEST.SYS: IRP_MJ_DEVICE_CONTROL\n")); 
 
    D12_IncrementIoCount(DeviceObject); 
 
    // 
    // Get a pointer to the current location in the Irp. This is where 
    //     the function codes and parameters are located. 
    // 
 
    deviceExtension = DeviceObject->DeviceExtension; 
     
    if (deviceExtension->AcceptingRequests == FALSE) { 
        ntStatus = STATUS_DEVICE_DATA_ERROR; 
        Irp->IoStatus.Status = ntStatus; 
        Irp->IoStatus.Information = 0; 
 
        IoCompleteRequest (Irp, 
                           IO_NO_INCREMENT 
                          ); 
 
        D12_DecrementIoCount(DeviceObject);                           
        return ntStatus; 
    } 
 
    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_D12_RESET_PIPE: 
        { 
        PD12_PIPE pipe; 
	    PFILE_OBJECT fileObject; 
 
		// get our context and see if it is a pipe 
	    fileObject = irpStack->FileObject; 
 
		pipe = (PD12_PIPE) fileObject->FsContext;     
 
		if(pipe == NULL) { 
			// error, this is not a pipe 
	        ntStatus = 
		        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 
		} else {             
            D12_ResetPipe(DeviceObject, pipe, TRUE); 
             
            ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS; 
        } 
        } 
        break; 
 
 
     case IOCTL_D12_GET_CONFIG_DESCRIPTOR: 
 
        // 
        // This api returns a copy of the configuration descriptor 
        // and all endpoint/interface descriptors. 
        // 
 
        // 
        // inputs  - none 
        // outputs - configuration descriptor plus interface 
        //          and endpoint descriptors 
        // 
 
        pch = (PUCHAR) ioBuffer; 
 
        configurationDescriptor = 
            D12_GetConfigDescriptor(DeviceObject); 
 
        if (configurationDescriptor) { 
             
            length = configurationDescriptor->wTotalLength; 
 
            RtlCopyMemory(pch, 
                          (PUCHAR) configurationDescriptor, 
                          length); 
 
            ExFreePool(configurationDescriptor); 
 
            Irp->IoStatus.Information = length; 
 
            ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS; 
        } 
        else { 
 
            Irp->IoStatus.Information = 0; 
 
            ntStatus = Irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; 
        } 
 
        break; 
 
 
     case IOCTL_D12_RESET_DEVICE: 
         
        { 
        NTSTATUS ntStatus; 
        ULONG portStatus; 
 
        D12_KdPrint (("D12TEST.SYS: Reset Device Test\n")); 
         
        TRAP(); // test this 
        // 
        // Check the port state, if it is disabled we will need  
        // to re-enable it 
        // 
        ntStatus = D12_GetPortStatus(DeviceObject, &portStatus); 
 
        if (NT_SUCCESS(ntStatus) && !(portStatus & USBD_PORT_ENABLED) && 
            portStatus & USBD_PORT_CONNECTED) { 
            // 
            // port is disabled, attempt reset 
            // 
            //D12_EnableParentPort(DeviceObject); 
            D12_ResetParentPort(DeviceObject); 
        } 
 
        } 
        break;                
 
     case IOCTL_D12_GET_DEVICE_DESCRIPTOR: 
 
 
        pch = (PUCHAR) ioBuffer; 
 
        deviceDescriptor = 
            deviceExtension->DeviceDescriptor; 
 
        if (deviceDescriptor) { 
             
            length = deviceDescriptor->bLength; 
 
            RtlCopyMemory(pch, 
                          (PUCHAR) deviceDescriptor, 
                          length); 
 
            Irp->IoStatus.Information = length; 
 
            ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS; 
        } 
        else { 
 
            Irp->IoStatus.Information = 0; 
 
            ntStatus = Irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR; 
        } 
 
        break; 
	 
	case IOCTL_READ_REGISTERS: 
		D12_ReadWriteRegister(DeviceObject, Irp, FALSE); 
		ntStatus = Irp->IoStatus.Status; 
 
		break; 
 
	case IOCTL_WRITE_REGISTERS: 
		D12_ReadWriteRegister(DeviceObject, Irp, TRUE); 
		ntStatus = Irp->IoStatus.Status; 
 
		break; 
 
	default: 
 
        ntStatus = 
            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; 
    } 
 
    IoCompleteRequest (Irp, 
                       IO_NO_INCREMENT 
                       ); 
 
    D12_DecrementIoCount(DeviceObject);                        
 
    return ntStatus; 
 
}