diff --git a/reactos/drivers/usb/directory.rbuild b/reactos/drivers/usb/directory.rbuild
index 46b5fa5cf49..084d6236cec 100644
--- a/reactos/drivers/usb/directory.rbuild
+++ b/reactos/drivers/usb/directory.rbuild
@@ -1,6 +1,9 @@
+
+
+
diff --git a/reactos/drivers/usb/nt4compat/directory.rbuild b/reactos/drivers/usb/nt4compat/directory.rbuild
new file mode 100644
index 00000000000..fda5da60b5a
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/directory.rbuild
@@ -0,0 +1,3 @@
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c b/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c
new file mode 100644
index 00000000000..d8f786449ca
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c
@@ -0,0 +1,909 @@
+/**
+ * bulkonly.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include "debug.h"
+#include "usb.h"
+#include "umss.h"
+#include
+
+#define OLYMPUS_CSW( pdev_EXT, staTUS ) \
+( ( ( pdev_EXT )->flags & UMSS_DEV_FLAG_OLYMPUS_DEV ) ? ( ( staTUS ) == CSW_OLYMPUS_SIGNATURE ) : FALSE )
+
+BOOL
+umss_clear_pass_through_length(
+PIO_PACKET io_packet
+);
+
+NTSTATUS
+umss_bulkonly_send_sense_req(
+PUMSS_DEVICE_EXTENSION pdev_ext
+);
+
+VOID
+umss_bulkonly_send_cbw_completion(
+IN PURB purb,
+IN PVOID context
+);
+
+VOID
+umss_bulkonly_transfer_data(
+PUMSS_DEVICE_EXTENSION pdev_ext
+);
+
+VOID
+umss_sync_submit_urb_completion(
+PURB purb,
+PVOID context
+);
+
+NTSTATUS
+umss_sync_submit_urb(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+PURB purb
+);
+
+VOID
+umss_bulkonly_get_status(
+PUMSS_DEVICE_EXTENSION pdev_ext
+);
+
+VOID
+umss_bulkonly_transfer_data_complete(
+PURB purb,
+PVOID reference
+);
+
+VOID
+umss_bulkonly_reset_pipe_and_get_status(
+IN PVOID reference
+);
+
+VOID
+umss_bulkonly_reset_recovery(
+IN PVOID reference
+);
+
+VOID
+umss_bulkonly_get_status_complete(
+IN PURB purb,
+IN PVOID context
+);
+
+NTSTATUS
+umss_bulkonly_startio(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN PIO_PACKET io_packet
+)
+/*++
+Routine Description:
+
+ Handler for all I/O requests using bulk-only protocol.
+
+ Arguments:
+
+ DeviceExtension - Device extension for our FDO.
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ PCOMMAND_BLOCK_WRAPPER cbw;
+ NTSTATUS status;
+
+ if( pdev_ext == NULL || io_packet == NULL || io_packet->pirp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ pdev_ext->retry = TRUE;
+ RtlCopyMemory( &pdev_ext->io_packet, io_packet, sizeof( pdev_ext->io_packet ) );
+
+ // Setup the command block wrapper for this request
+ cbw = &pdev_ext->cbw;
+ cbw->dCBWSignature = CBW_SIGNATURE;
+ cbw->dCBWTag = 0;
+ cbw->dCBWDataTransferLength = io_packet->data_length;
+ cbw->bmCBWFlags = ( io_packet->flags & USB_DIR_IN ) ? 0x80 : 0;
+ cbw->bCBWLun = io_packet->lun;
+ cbw->bCBWLength = io_packet->cdb_length;
+ RtlCopyMemory( cbw->CBWCB, io_packet->cdb, sizeof( cbw->CBWCB ) );
+
+ RtlZeroMemory( &pdev_ext->csw, sizeof( pdev_ext->csw ) );
+ // Send the command block wrapper to the device.
+ // Calls UMSS_BulkOnlySendCBWComplete when transfer completes.
+ status = umss_bulk_transfer(
+ pdev_ext,
+ USB_DIR_OUT,
+ cbw,
+ sizeof( COMMAND_BLOCK_WRAPPER ),
+ umss_bulkonly_send_cbw_completion
+ );
+
+ return status;
+}
+
+
+NTSTATUS
+umss_bulk_transfer(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN UCHAR trans_dir,
+IN PVOID buf,
+IN ULONG buf_length,
+IN PURBCOMPLETION completion
+)
+{
+ PURB purb;
+ NTSTATUS status;
+ DEV_HANDLE endp_handle;
+
+ if( pdev_ext == NULL || buf == NULL || completion == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( buf_length > ( ULONG )MAX_BULK_TRANSFER_LENGTH )
+ return STATUS_INVALID_PARAMETER;
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+
+ if( purb == NULL )
+ return STATUS_NO_MEMORY;
+
+ if( trans_dir == USB_DIR_OUT )
+ {
+ endp_handle = usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->out_endp_idx );
+ }
+ else
+ {
+ endp_handle = usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->in_endp_idx );
+ }
+
+ UsbBuildInterruptOrBulkTransferRequest( purb,
+ endp_handle,
+ buf,
+ buf_length,
+ completion,
+ pdev_ext,
+ 0 );
+ dev_mgr_register_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb );
+ status = usb_submit_urb( pdev_ext->dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ return status;
+ }
+
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+ if( purb )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ }
+ return status;
+}
+
+VOID
+umss_bulkonly_send_cbw_completion(
+IN PURB purb,
+IN PVOID context
+)
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )context;
+
+ status = purb->status;
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_SENSE )
+ {
+ usb_free_mem( purb->data_buffer );
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ }
+
+ if ( status != STATUS_SUCCESS )
+ {
+ if ( usb_halted( status ) )
+ {
+ //Schedule a work-item to do a reset recovery
+ if( !umss_schedule_workitem( (PVOID)pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ }
+ else
+ {
+ // Device failed CBW without stalling, so complete with error
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ else
+ {
+ // CBW was accepted by device, so start data phase of I/O operation
+ umss_bulkonly_transfer_data( pdev_ext );
+ }
+
+ if( purb )
+ usb_free_mem( purb );
+
+ purb = NULL;
+ return;
+}
+
+NTSTATUS
+umss_sync_submit_urb(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+PURB purb
+)
+//can only be called at passive level
+{
+ NTSTATUS status;
+
+ if( pdev_ext == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ purb->completion = umss_sync_submit_urb_completion;
+ purb->context = ( PVOID )pdev_ext;
+
+ dev_mgr_register_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb );
+ status = usb_submit_urb( pdev_ext->dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ KeWaitForSingleObject(
+ &pdev_ext->sync_event,
+ Executive,
+ KernelMode,
+ TRUE,
+ NULL );
+ status = purb->status;
+ }
+ else
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ return status;
+}
+
+VOID
+umss_sync_submit_urb_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )context;
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+ KeSetEvent( &pdev_ext->sync_event, 0, FALSE );
+ return;
+}
+
+VOID
+umss_bulkonly_reset_recovery(
+IN PVOID reference
+)
+/*++
+Routine Description:
+
+ Worker function used to execute a reset recovery after a stall.
+
+ Arguments:
+
+ Reference - Our device extension.
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ URB urb;
+ NTSTATUS status;
+ DEV_HANDLE endp_handle;
+
+ pdev_ext = (PUMSS_DEVICE_EXTENSION)reference;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_bulkonly_reset_recovery(): entering...\n" ) );
+ // Steps for reset recovery:
+ // 1. Send device a mass storage reset command on the default endpoint.
+ // 2. Reset the bulk-in endpoint.
+ // 3. Reset the bulk-out endpoint.
+ // 4. Complete the original I/O request with error.
+
+
+ // Build the mass storage reset command
+ UsbBuildVendorRequest(
+ &urb,
+ pdev_ext->dev_handle | 0xffff, //default pipe
+ NULL, //no extra data
+ 0, //no size
+ 0x21, //class, interface
+ BULK_ONLY_MASS_STORAGE_RESET,
+ 0,
+ pdev_ext->pif_desc->bInterfaceNumber,
+ NULL, //completion
+ NULL, //context
+ 0 ); //reference
+
+ // Send mass storage reset command to device
+ status = umss_sync_submit_urb( pdev_ext, &urb);
+
+ if ( status != STATUS_SUCCESS )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_recovery(): Reset Recovery failed!\n"));
+ }
+ else
+ {
+ //Reset Bulk-in endpoint
+ endp_handle = usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->in_endp_idx );
+ status = umss_reset_pipe(
+ pdev_ext,
+ endp_handle
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_recovery(): Unable to clear Bulk-in endpoint\n"));
+ }
+
+ //Reset Bulk-out endpoint
+ endp_handle = usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->out_endp_idx );
+ status = umss_reset_pipe(
+ pdev_ext,
+ endp_handle
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_recovery(): Unable to clear Bulk-out endpoint\n"));
+ }
+ }
+ umss_complete_request( pdev_ext, status );
+}
+
+
+VOID
+umss_bulkonly_transfer_data(
+PUMSS_DEVICE_EXTENSION pdev_ext
+)
+/*++
+Routine Description:
+
+ Schedules a bulk data transfer to/from the device.
+
+ Arguments:
+
+ DeviceExtension - Our FDO's device extension.
+
+Return Value:
+
+ NONE
+
+--*/
+
+{
+ PVOID data_buf;
+ ULONG data_buf_length;
+ NTSTATUS status;
+ UCHAR trans_dir;
+
+ // Steps for data phase
+ // 1. Get data buffer fragment (either SGD list, flat buffer, or none).
+ // 2. Schedule data transfer if neccessary.
+ // 3. Repeat 1-2 until all data transferred, or endpoint stalls.
+ // 4. Move to status phase.
+
+ // Get next data buffer element, if any
+ data_buf = umss_get_buffer( pdev_ext, &data_buf_length);
+
+ if (NULL == data_buf)
+ {
+ //No data to transfer, so move to status phase
+ umss_bulkonly_get_status( pdev_ext );
+ }
+ else
+ {
+ // Schedule the data transfer.
+ // Calls umss_bulkonly_transfer_data_complete when transfer completes.
+
+ if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_NORMAL )
+ trans_dir = ( UCHAR )( ( pdev_ext->cbw.bmCBWFlags & USB_DIR_IN ) ? USB_DIR_IN : USB_DIR_OUT );
+ else if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_SENSE )
+ trans_dir = USB_DIR_IN;
+
+ if( ( status = umss_bulk_transfer(
+ pdev_ext,
+ trans_dir,
+ data_buf,
+ data_buf_length,
+ umss_bulkonly_transfer_data_complete )
+ ) != STATUS_PENDING )
+ {
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ return;
+}
+
+
+VOID
+umss_bulkonly_transfer_data_complete(
+PURB purb,
+PVOID reference
+)
+/*++
+Routine Description:
+ Completion handler for bulk data transfer requests.
+--*/
+
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )reference;
+
+ status = purb->status;
+
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ if ( status != STATUS_SUCCESS )
+ {
+ //
+ // clear the data length if this is a scsi pass through request
+ //
+ umss_clear_pass_through_length( &pdev_ext->io_packet );
+
+ // Device failed data phase
+ // Check if we need to clear stalled pipe
+ if ( usb_halted( status ) )
+ {
+ PULONG buf;
+ buf = usb_alloc_mem( NonPagedPool, 32 );
+ buf[ 0 ] = ( ULONG )pdev_ext;
+ buf[ 1 ] = ( ULONG )purb->endp_handle;
+
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_transfer_data_complete(): transfer data error!\n"));
+ if (!umss_schedule_workitem( ( PVOID )buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ usb_free_mem( buf ), buf = NULL;
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
+ TRAP();
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ else
+ {
+ //finish our request
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ else
+ {
+ // Start next part of data phase
+ //umss_bulkonly_transfer_data( pdev_ext );
+ umss_bulkonly_get_status( pdev_ext );
+ //umss_complete_request( pdev_ext, status );
+ }
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ return; // STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+VOID
+umss_bulkonly_reset_pipe_and_get_status(
+IN PVOID reference
+)
+{
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ DEV_HANDLE endp_handle;
+ NTSTATUS status;
+
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_pipe_and_get_status(): entering...\n") );
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )( ( ( PULONG )reference )[ 0 ] );
+ endp_handle = ( DEV_HANDLE )( ( PULONG )reference )[ 1 ];
+ usb_free_mem( reference );
+ reference = NULL;
+
+ // Reset the endpoint
+ if( ( status = umss_reset_pipe( pdev_ext, endp_handle ) ) != STATUS_SUCCESS )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_pipe_and_get_status(): reset pipe failed\n") );
+ umss_complete_request( pdev_ext, status );
+ return;
+ }
+ // Data phase is finished since the endpoint stalled, so go to status phase
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_reset_pipe_and_get_status(): reset pipe succeeds, continue to get status\n") );
+ umss_bulkonly_get_status( pdev_ext );
+}
+
+VOID
+umss_bulkonly_get_status(
+PUMSS_DEVICE_EXTENSION pdev_ext
+)
+{
+ NTSTATUS status;
+ // Schedule bulk transfer to get command status wrapper from device
+ status = umss_bulk_transfer(
+ pdev_ext,
+ USB_DIR_IN,
+ &( pdev_ext->csw ),
+ sizeof( COMMAND_STATUS_WRAPPER ),
+ umss_bulkonly_get_status_complete
+ );
+ if( status != STATUS_PENDING )
+ {
+ umss_complete_request( pdev_ext, status );
+ }
+}
+
+
+VOID
+umss_bulkonly_get_status_complete(
+IN PURB purb,
+IN PVOID context
+)
+/*++
+Routine Description:
+
+ Completion handler for bulk data transfer request.
+
+ Arguments:
+
+ DeviceObject - Previous device object.
+ Irp - Irp used for sending command.
+ Reference - Our FDO.
+
+Return Value:
+
+ Driver-originated IRPs always return STATUS_MORE_PROCESSING_REQUIRED.
+
+--*/
+
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PCOMMAND_STATUS_WRAPPER csw;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION ) context;
+ status = purb->status;
+
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ csw = &( pdev_ext->csw );
+ if ( status == STATUS_SUCCESS &&
+ ( ( csw->dCSWSignature == CSW_SIGNATURE ) || OLYMPUS_CSW( pdev_ext, csw->dCSWSignature ) ) )
+ {
+ if ( csw->bCSWStatus == CSW_STATUS_PASSED )
+ {
+ // Received valid CSW with good status
+
+ if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_NORMAL &&
+ ( pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE ) &&
+ pdev_ext->io_packet.sense_data != NULL )
+ UMSS_FORGE_GOOD_SENSE( pdev_ext->io_packet.sense_data )
+
+ umss_complete_request( pdev_ext, STATUS_SUCCESS );
+ }
+ else if ( csw->bCSWStatus == CSW_STATUS_FAILED )
+ {
+ // start a request sense if necessary
+ if( ( pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE ) &&
+ ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_NORMAL )
+ {
+ if( umss_bulkonly_send_sense_req( pdev_ext ) != STATUS_PENDING )
+ {
+ // don't know how to handle.
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ else
+ {
+ // fall through to free the urb
+ }
+ }
+ else
+ {
+ // error occurred, reset device
+ if( !umss_schedule_workitem( (PVOID)pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ }
+ }
+ else
+ {
+ // error occurred, reset device
+ if( !umss_schedule_workitem( (PVOID)pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ }
+ }
+ else if ( ( status != STATUS_SUCCESS ) && ( usb_halted( status ) ) && ( pdev_ext->retry ) )
+ {
+ // Device stalled CSW transfer, retry once before failing
+ PULONG buf;
+ pdev_ext->retry = FALSE;
+
+ buf = usb_alloc_mem( NonPagedPool, 32 );
+ buf[ 0 ] = ( ULONG )pdev_ext;
+ buf[ 1 ] = ( ULONG )purb->endp_handle;
+
+ if ( !umss_schedule_workitem( ( PVOID )buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ usb_free_mem( buf ), buf = NULL;
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
+ TRAP();
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ else if( status != STATUS_CANCELLED )
+ {
+ // An error has occured. Reset the device.
+ if ( !umss_schedule_workitem( ( PVOID )pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_status_complete(): Failed to schedule work-item to reset pipe!\n"));
+ TRAP();
+ umss_complete_request( pdev_ext, status );
+ }
+ }
+ else
+ {
+ // the request is canceled
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_status_complete(): the request is canceled\n"));
+ umss_complete_request( pdev_ext, STATUS_CANCELLED );
+ }
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ return;
+}
+
+
+CHAR
+umss_bulkonly_get_maxlun(
+IN PUMSS_DEVICE_EXTENSION pdev_ext
+)
+/*++
+Routine Description:
+
+ Queries Bulk-Only device for maximum LUN number
+
+ Arguments:
+
+ DeviceExtension - Our device extension.
+
+Return Value:
+
+ Maximum LUN number for device, or 0 if error occurred.
+
+--*/
+
+{
+ PURB purb=NULL;
+ CHAR max_lun;
+ NTSTATUS status;
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+
+ if (!purb)
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_maxlun(): Failed to allocate URB, setting max LUN to 0\n"));
+ max_lun = 0;
+ }
+ else
+ {
+ // Build the get max lun command
+ UsbBuildVendorRequest(
+ purb,
+ ( pdev_ext->dev_handle | 0xffff ),
+ &max_lun,
+ sizeof( max_lun ),
+ 0xb1, //class, interface, in
+ BULK_ONLY_GET_MAX_LUN,
+ 0,
+ pdev_ext->pif_desc->bInterfaceNumber,
+ NULL,
+ NULL,
+ 0);
+
+ // Send get max lun command to device
+ status = umss_sync_submit_urb( pdev_ext, purb);
+
+ if ( status != STATUS_PENDING )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_maxlun(): Get Max LUN command failed, setting max LUN to 0!\n"));
+ max_lun = 0;
+ }
+ }
+
+ if ( purb )
+ usb_free_mem( purb );
+
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_bulkonly_get_maxlun(): Max LUN = %x\n", max_lun));
+
+ return max_lun;
+}
+
+PVOID
+umss_get_buffer(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+ULONG* buf_length
+)
+{
+ PVOID buffer;
+
+ if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_NORMAL )
+ {
+ buffer = (PVOID)pdev_ext->io_packet.data_buffer;
+ *buf_length = pdev_ext->io_packet.data_length;
+ }
+ else if( ( pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK ) == IOP_FLAG_STAGE_SENSE )
+ {
+ buffer = (PVOID)pdev_ext->io_packet.sense_data;
+ *buf_length = pdev_ext->io_packet.sense_data_length;
+ }
+
+ return buffer;
+}
+
+BOOL
+umss_bulkonly_build_sense_cdb(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+PCOMMAND_BLOCK_WRAPPER cbw
+)
+{
+ UCHAR sub_class;
+ PUCHAR cdb;
+
+ if( pdev_ext == NULL || cbw == NULL )
+ return FALSE;
+
+ cdb = cbw->CBWCB;
+ RtlZeroMemory( cdb, MAX_CDB_LENGTH );
+ sub_class = pdev_ext->pif_desc->bInterfaceSubClass;
+
+ cdb[ 0 ] = SFF_REQUEST_SENSEE;
+ cdb[ 1 ] = pdev_ext->io_packet.lun << 5;
+ cdb[ 4 ] = 18;
+
+ switch( sub_class )
+ {
+ case UMSS_SUBCLASS_SFF8070I:
+ case UMSS_SUBCLASS_UFI:
+ {
+ cbw->bCBWLength = 12;
+ break;
+ }
+ case UMSS_SUBCLASS_RBC:
+ case UMSS_SUBCLASS_SCSI_TCS:
+ {
+ cbw->bCBWLength = 6;
+ break;
+ }
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+NTSTATUS
+umss_bulkonly_send_sense_req(
+PUMSS_DEVICE_EXTENSION pdev_ext
+)
+{
+ PCOMMAND_BLOCK_WRAPPER cbw;
+ NTSTATUS status;
+
+ if( pdev_ext == NULL || pdev_ext->io_packet.sense_data == NULL || pdev_ext->io_packet.sense_data_length < 18 )
+ return STATUS_INVALID_PARAMETER;
+
+ pdev_ext->retry = TRUE;
+
+ cbw = usb_alloc_mem( NonPagedPool, sizeof( COMMAND_BLOCK_WRAPPER ) );
+ RtlZeroMemory( cbw, sizeof( COMMAND_BLOCK_WRAPPER ) );
+ pdev_ext->io_packet.flags &= ~IOP_FLAG_STAGE_MASK;
+ pdev_ext->io_packet.flags |= IOP_FLAG_STAGE_SENSE;
+
+ cbw->dCBWSignature = CBW_SIGNATURE;
+ cbw->dCBWTag = 0;
+ cbw->dCBWDataTransferLength = pdev_ext->io_packet.sense_data_length;
+ cbw->bmCBWFlags = USB_DIR_IN;
+ cbw->bCBWLun = 0;
+
+ if( umss_bulkonly_build_sense_cdb( pdev_ext, cbw ) == FALSE )
+ {
+ usb_free_mem( cbw );
+ cbw = NULL;
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ status = umss_bulk_transfer(
+ pdev_ext,
+ USB_DIR_OUT,
+ cbw,
+ sizeof( COMMAND_BLOCK_WRAPPER ),
+ umss_bulkonly_send_cbw_completion
+ );
+
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( cbw );
+ cbw = NULL;
+ }
+ return status;
+}
+
+BOOL
+umss_clear_pass_through_length(
+PIO_PACKET io_packet
+)
+{
+ //
+ // clear the respective data length to meet request of scsi pass through requirement.
+ //
+
+ BOOL sense_stage;
+ ULONG ctrl_code;
+ PIO_STACK_LOCATION cur_stack;
+ PSCSI_PASS_THROUGH pass_through;
+ PSCSI_PASS_THROUGH_DIRECT pass_through_direct;
+
+ if( io_packet == NULL )
+ return FALSE;
+
+ if( ( io_packet->flags & IOP_FLAG_SCSI_CTRL_TRANSFER ) == 0 )
+ return FALSE;
+
+ sense_stage = FALSE;
+ if( io_packet->flags & IOP_FLAG_STAGE_SENSE )
+ sense_stage = TRUE;
+
+ cur_stack = IoGetCurrentIrpStackLocation( io_packet->pirp );
+ ctrl_code = cur_stack->Parameters.DeviceIoControl.IoControlCode;
+ if( ctrl_code == IOCTL_SCSI_PASS_THROUGH_DIRECT )
+ {
+ pass_through_direct = io_packet->pirp->AssociatedIrp.SystemBuffer;
+ if( sense_stage )
+ pass_through_direct->SenseInfoLength = 0;
+ else
+ pass_through_direct->DataTransferLength = 0;
+ }
+ else if( ctrl_code == IOCTL_SCSI_PASS_THROUGH )
+ {
+ pass_through= io_packet->pirp->AssociatedIrp.SystemBuffer;
+ if( sense_stage )
+ pass_through->SenseInfoLength = 0;
+ else
+ pass_through->DataTransferLength = 0;
+ }
+ else
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/cbi.c b/reactos/drivers/usb/nt4compat/usbdriver/cbi.c
new file mode 100644
index 00000000000..0911f85ba38
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/cbi.c
@@ -0,0 +1,363 @@
+/**
+ * cbi.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include "debug.h"
+#include "umss.h"
+
+VOID
+umss_cbi_send_adsc_complete(
+PURB purb,
+PVOID context
+);
+
+VOID
+umss_cbi_transfer_data(
+PUMSS_DEVICE_EXTENSION pdev_ext
+);
+
+VOID
+umss_cbi_get_status(
+PUMSS_DEVICE_EXTENSION pdev_ext
+);
+
+VOID
+umss_cbi_transfer_data_complete(
+PURB purb,
+PVOID context
+);
+
+VOID
+umss_cbi_get_status_complete(
+PURB purb,
+PVOID context
+);
+
+NTSTATUS
+umss_class_specific_request(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN UCHAR request,
+IN UCHAR dir,
+IN PVOID buffer,
+IN ULONG buffer_length,
+IN PURBCOMPLETION completion
+)
+{
+ PURB purb;
+ NTSTATUS status;
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ // Build URB for the ADSC command
+ UsbBuildVendorRequest(
+ purb,
+ pdev_ext->dev_handle | 0xffff,
+ buffer,
+ buffer_length,
+ 0x21,
+ request,
+ 0,
+ pdev_ext->pif_desc->bInterfaceNumber,
+ completion,
+ pdev_ext,
+ 0 );
+
+ status = usb_submit_urb( pdev_ext->dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ return status;
+ }
+ dev_mgr_register_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb );
+ return status;
+}
+
+NTSTATUS
+umss_cbi_startio(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN PIO_PACKET io_packet
+)
+{
+ NTSTATUS status;
+
+ status = STATUS_NOT_SUPPORTED;
+ return status;
+
+ RtlCopyMemory( &pdev_ext->io_packet, io_packet, sizeof( pdev_ext->io_packet ) );
+
+ // Send the ADSC request to the device
+ // Calls UMSS_CbiSendADSCComplete when transfer completes
+ status = umss_class_specific_request(
+ pdev_ext,
+ ACCEPT_DEVICE_SPECIFIC_COMMAND,
+ USB_DIR_OUT ,
+ io_packet->cdb,
+ io_packet->cdb_length,
+ umss_cbi_send_adsc_complete
+ );
+
+ return status;
+}
+
+
+
+VOID
+umss_cbi_send_adsc_complete(
+PURB purb,
+PVOID context
+)
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpStack;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PIO_PACKET io_packet;
+ ULONG bytes_to_transfer;
+ PUCHAR buf;
+
+ pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
+ io_packet = &pdev_ext->io_packet;
+
+ status = purb->status;
+
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ if (!usb_success( status ) )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_cbi_send_adsc_complete(): Command Block Failure!!!\n"));
+
+ // BUGBUG - Should reset device here?
+ // Device failed Command Block, complete with error
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+
+ }
+ else if ( io_packet->data_length )
+ {
+
+ usb_dbg_print( DBGLVL_HIGH,("umss_cbi_send_adsc_complete(): Queuing Data Transfer DPC\n"));
+ umss_cbi_transfer_data( pdev_ext );
+
+ }
+ else if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
+ {
+ // Device supports interrupt pipe, so get status
+ umss_cbi_get_status( pdev_ext );
+ }
+ else
+ {
+ // Device does not report status, so complete request
+ umss_complete_request( pdev_ext, STATUS_SUCCESS );
+ }
+
+ usb_free_mem( purb );
+ purb = NULL;
+}
+
+
+
+VOID
+umss_cbi_reset_pipe(
+IN PVOID reference
+)
+
+{
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
+
+ // Reset the appropriate pipe, based on data direction
+ umss_reset_pipe(
+ pdev_ext,
+ ( pdev_ext->io_packet.flags & USB_DIR_IN ) ?
+ usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->in_endp_idx ):
+ usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->out_endp_idx )
+ );
+
+ // Device stalled endpoint, so complete I/O operation with error.
+ // BUGBUG is this correct? Check spec...
+ umss_complete_request( pdev_ext, USB_STATUS_STALL_PID );
+}
+
+VOID
+umss_cbi_transfer_data(
+PUMSS_DEVICE_EXTENSION pdev_ext
+)
+{
+ PVOID buffer;
+ ULONG buffer_length;
+
+ // Get next data buffer element, if any.
+ buffer = umss_get_buffer( pdev_ext, &buffer_length );
+ if (NULL == buffer)
+ {
+ //Done with data phase, so move to status phase if (supported)
+
+ if (pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI)
+ {
+ // Device supports interrupt pipe, so get status
+ umss_cbi_get_status(pdev_ext);
+ }
+ else
+ {
+ // No interrupt pipe, so just complete the request
+ umss_complete_request(pdev_ext, STATUS_SUCCESS);
+ }
+ }
+ else
+ {
+ // Transfer next element of the data phase
+ umss_bulk_transfer(
+ pdev_ext,
+ (UCHAR)((pdev_ext->io_packet.flags & USB_DIR_IN ) ? USB_DIR_IN : USB_DIR_OUT ),
+ buffer,
+ buffer_length,
+ umss_cbi_transfer_data_complete
+ );
+ }
+}
+
+
+VOID
+umss_cbi_transfer_data_complete(
+PURB purb,
+PVOID context
+)
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
+ status = purb->status;
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ if ( !usb_success( status ) )
+ {
+ // Device failed Data Transfer
+ // Check if we need to clear stalled pipe
+ if ( usb_halted( status ) )
+ {
+ // Reset pipe can only be done at passive level, so we need
+ // to schedule a work item to do it.
+ if ( !umss_schedule_workitem( ( PVOID )pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_cbi_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
+ TRAP();
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ }
+ else
+ {
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ return;
+ }
+ // Transfer succeeded
+ // umss_cbi_transfer_data( pdev_ext );
+ umss_complete_request( pdev_ext, STATUS_SUCCESS );
+ return;
+}
+
+
+VOID
+umss_cbi_get_status(
+PUMSS_DEVICE_EXTENSION pdev_ext
+)
+{
+ PURB purb;
+ NTSTATUS status;
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return;
+
+ // Build a URB for our interrupt transfer
+ UsbBuildInterruptOrBulkTransferRequest(
+ purb,
+ usb_make_handle( ( pdev_ext->dev_handle >> 16 ), pdev_ext->if_idx, pdev_ext->int_endp_idx ),
+ ( PUCHAR )&pdev_ext->idb,
+ sizeof( INTERRUPT_DATA_BLOCK ),
+ umss_cbi_get_status_complete,
+ pdev_ext,
+ 0
+ );
+
+ // Call USB driver stack
+ status = usb_submit_urb( pdev_ext->dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ return;
+ }
+ dev_mgr_register_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb );
+ return;
+}
+
+
+VOID
+umss_cbi_get_status_complete(
+PURB purb,
+PVOID context
+)
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PINTERRUPT_DATA_BLOCK idb;
+
+ pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
+
+ status = purb->status;
+ dev_mgr_remove_irp( pdev_ext->dev_mgr, pdev_ext->io_packet.pirp );
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ if ( !usb_success( status ) )
+ {
+ // Device failed Data Transfer
+ // Check if we need to clear stalled pipe
+ if ( usb_halted( status ) )
+ {
+ if ( !umss_schedule_workitem( (PVOID)pdev_ext, umss_cbi_reset_pipe, pdev_ext->dev_mgr, pdev_ext->dev_handle ) )
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_cbi_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
+ TRAP();
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ return;
+ }
+ }
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ return;
+ }
+
+ // Interrupt transfer succeeded
+ idb = &(pdev_ext->idb);
+
+ // Check for an error in the status block
+ if ( ( 0 != idb->bType ) || ( 0 != ( idb->bValue & 0x3 ) ) )
+ {
+ umss_complete_request( pdev_ext, STATUS_IO_DEVICE_ERROR );
+ }
+ else
+ {
+ umss_complete_request( pdev_ext, STATUS_SUCCESS );
+ }
+}
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c b/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c
new file mode 100644
index 00000000000..732a77c2ac4
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/compdrv.c
@@ -0,0 +1,566 @@
+/**
+ * compdrv.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//this driver is part of the dev manager responsible to manage if device
+
+#include "td.h"
+#include "ntddk.h"
+#include "umss.h"
+#include "usb.h"
+#include "hub.h"
+#include "debug.h"
+
+VOID
+compdev_set_cfg_completion(
+PURB purb,
+PVOID context
+);
+
+VOID
+compdev_select_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+compdev_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+compdev_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+compdev_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+compdev_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
+ pdriver->driver_desc.release_num = 0x100; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 0; // Configuration Value
+ pdriver->driver_desc.if_num = 0; // Interface Number
+ pdriver->driver_desc.if_class = 0; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB composit dev driver"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ //we have no extra data sturcture currently
+ pdriver->driver_ext = NULL;
+ pdriver->driver_ext_size = 0;
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = compdev_connect;
+ pdriver->disp_tbl.dev_disconnect = compdev_disconnect;
+ pdriver->disp_tbl.dev_stop = compdev_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ return TRUE;
+}
+
+BOOL
+compdev_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ return TRUE;
+}
+
+BOOL
+compdev_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+)
+{
+ PURB purb;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ PUCHAR buf;
+ LONG credit, i, j, match;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ if( param == NULL || dev_handle == 0 )
+ return FALSE;
+
+ dev_mgr = param->dev_mgr;
+
+ // let's set the configuration
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return FALSE;
+
+ buf = usb_alloc_mem( NonPagedPool, 512 );
+ if( buf == NULL )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): can not alloc buf\n" ) );
+ usb_free_mem( purb );
+ return FALSE;
+ }
+
+ // before we set the configuration, let's search to find if there
+ // exist interfaces we supported
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+ purb->endp_handle = dev_handle | 0xffff;
+ purb->data_buffer = buf;
+ purb->data_length = 512;
+ purb->completion = NULL; // this is an immediate request, no completion required
+ purb->context = NULL;
+ purb->reference = 0;
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = USB_DT_CONFIG << 8;
+ psetup->wIndex = 0;
+ psetup->wLength = 512;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ TRAP();
+ usb_free_mem( buf );
+ usb_free_mem( purb );
+ return FALSE;
+ }
+
+ // let's scan the interfacs for those we recognize
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf;
+ if( pconfig_desc->wTotalLength > 512 )
+ {
+ usb_free_mem( buf );
+ usb_free_mem( purb );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): error, bad configuration desc\n" ) );
+ return FALSE;
+ }
+
+ pif_desc = ( PUSB_INTERFACE_DESC )&pconfig_desc[ 1 ];
+ for( i = 0, credit = 0; i < ( LONG )pconfig_desc->bNumInterfaces; i++ )
+ {
+ for( j = 0; j < DEVMGR_MAX_DRIVERS; j++ )
+ {
+ credit = dev_mgr_score_driver_for_if( dev_mgr, &dev_mgr->driver_list[ j ], pif_desc );
+ if( credit )
+ break;
+ }
+ if( credit )
+ break;
+
+ if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) )
+ break;
+ }
+
+ i = pconfig_desc->bConfigurationValue;
+ usb_free_mem( buf );
+ buf = NULL;
+ if( credit == 0 )
+ {
+ usb_free_mem( purb );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): oops..., no supported interface found\n" ) );
+ return FALSE;
+ }
+
+ //set the configuration
+ urb_init( purb );
+ purb->endp_handle = dev_handle | 0xffff;
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->completion = compdev_set_cfg_completion;
+ purb->context = dev_mgr;
+ purb->reference = ( ULONG )param->pdriver;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_CONFIGURATION;
+ psetup->wValue = ( USHORT ) i;
+ psetup->wIndex = 0;
+ psetup->wLength = 0;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_connect(): start config the device, cfgval=%d\n", i ) );
+ status = usb_submit_urb( dev_mgr, purb );
+
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+
+ if( status == STATUS_SUCCESS )
+ return TRUE;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+VOID
+compdev_event_select_if_driver(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context,
+ULONG param
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ DEV_HANDLE dev_handle;
+ PUMSS_CREATE_DATA cd;
+
+ if( pdev == NULL )
+ return;
+
+ //
+ // RtlZeroMemory( &cd, sizeof( cd ) );
+ //
+ dev_mgr = dev_mgr_from_dev( pdev );
+ dev_handle = usb_make_handle( pdev->dev_id, 0, 0 );
+ compdev_select_driver( dev_mgr, dev_handle );
+ return;
+}
+
+BOOL
+compdev_post_event_select_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ PUSB_EVENT pevent;
+ BOOL bret;
+ PUSB_DEV pdev;
+
+ USE_IRQL;
+
+ if( dev_mgr == NULL || dev_handle == 0 )
+ return FALSE;
+
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS )
+ return FALSE;
+
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ bret = FALSE;
+ goto LBL_OUT;
+ }
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ {
+ bret = FALSE;
+ goto LBL_OUT;
+ }
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->event = USB_EVENT_DEFAULT;
+ pevent->pdev = pdev;
+ pevent->context = 0;
+ pevent->param = 0;
+ pevent->pnext = 0; //vertical queue for serialized operation
+ pevent->process_event = compdev_event_select_if_driver;
+ pevent->process_queue = event_list_default_process_queue;
+
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE ); // wake up the dev_mgr_thread
+ bret = TRUE;
+
+ LBL_OUT:
+
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ usb_unlock_dev( pdev );
+ return bret;
+}
+
+VOID
+compdev_set_cfg_completion(
+PURB purb,
+PVOID context
+)
+{
+ DEV_HANDLE dev_handle;
+ PUSB_DEV_MANAGER dev_mgr;
+ PWORK_QUEUE_ITEM pwork_item;
+ PUSB_DRIVER pdriver;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+
+ USE_IRQL;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ dev_handle = purb->endp_handle & ~0xffff;
+ dev_mgr = ( PUSB_DEV_MANAGER ) context;
+ pdriver = ( PUSB_DRIVER )purb->reference;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ // set the dev state
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ return;
+ }
+ // safe to release the pdev ref since we are in urb completion
+ usb_unlock_dev( pdev );
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ return;
+ }
+
+ if( dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE )
+ return;
+
+ //transit the state to configured
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+ unlock_dev( pdev, TRUE );
+
+ //
+ // we change to use our thread for driver choosing. it will reduce
+ // the race condition when different pnp event comes simultaneously
+ //
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_set_cfg_completion(): start select driver for the dev\n" ) );
+ compdev_post_event_select_driver( dev_mgr, dev_handle );
+
+ return;
+}
+
+VOID
+compdev_select_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ URB urb;
+ LONG i, j, k, credit;
+ ULONG dev_id;
+ PUCHAR buf;
+ NTSTATUS status;
+ PUSB_DRIVER pcand, ptemp_drv;
+
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_DEV pdev;
+
+ USE_IRQL;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): entering...\n" ) );
+
+ dev_id = dev_handle >> 16;
+
+ buf = usb_alloc_mem( NonPagedPool, 512 );
+
+ if( buf == NULL )
+ return;
+
+ // now let's get the descs, one configuration
+ urb_init( &urb );
+ psetup = ( PUSB_CTRL_SETUP_PACKET )urb.setup_packet;
+ urb.endp_handle = dev_handle | 0xffff;
+ urb.data_buffer = buf;
+ urb.data_length = 512;
+ urb.completion = NULL; // this is an immediate request, no completion required
+ urb.context = NULL;
+ urb.reference = 0;
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = USB_DT_CONFIG << 8;
+ psetup->wIndex = 0;
+ psetup->wLength = 512;
+
+ status = usb_submit_urb( dev_mgr, &urb );
+ if( status == STATUS_PENDING )
+ {
+ TRAP();
+ }
+
+ // let's scan the interfacs for those we recognize
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf;
+ if( pconfig_desc->wTotalLength > 512 )
+ {
+ usb_free_mem( buf );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): error, bad configuration desc\n" ) );
+ return;
+ }
+ pif_desc = ( PUSB_INTERFACE_DESC )&pconfig_desc[ 1 ];
+
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS )
+ {
+ usb_free_mem( buf );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "compdev_select_driver(): error, dev does not exist\n" ) );
+ return;
+ }
+
+ for( i = 0; i < ( LONG )pconfig_desc->bNumInterfaces; i++ )
+ {
+ for( j = 0, credit = 0, pcand = NULL; j < DEVMGR_MAX_DRIVERS; j++ )
+ {
+ ptemp_drv = &dev_mgr->driver_list[ j ];
+ k = dev_mgr_score_driver_for_if( dev_mgr, ptemp_drv, pif_desc );
+ if( k > credit )
+ credit = k, pcand = ptemp_drv;
+ }
+
+ if( credit )
+ {
+ // ok, we find one
+ CONNECT_DATA param;
+
+ if( pcand->disp_tbl.dev_connect )
+ {
+ param.dev_mgr = dev_mgr;
+ param.pdriver = pcand;
+ param.dev_handle = 0;
+ pcand->disp_tbl.dev_connect( ¶m, usb_make_handle( dev_id, i, 0 ) );
+ }
+ }
+ if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE )
+ {
+ break;
+ }
+ }
+ usb_unlock_dev( pdev );
+
+ if( buf )
+ {
+ usb_free_mem( buf );
+ buf = NULL;
+ }
+ return;
+}
+
+BOOL
+compdev_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ PUSB_DEV pdev;
+ LONG i;
+ ULONG dev_id;
+ PUSB_DRIVER pdrv;
+ NTSTATUS status;
+
+ if( dev_mgr == NULL || dev_handle == 0 )
+ return FALSE;
+
+ pdev = NULL;
+ dev_id = dev_handle >> 16;
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( pdev )
+ {
+ if( pdev->usb_config )
+ {
+ for( i = 0; i < pdev->usb_config->if_count; i++ )
+ {
+ if( pdrv = pdev->usb_config->interf[ i ].pif_drv )
+ {
+ pdrv->disp_tbl.dev_stop( dev_mgr, usb_make_handle( dev_id, i, 0 ) );
+ }
+ }
+ }
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ }
+ return TRUE;
+}
+
+BOOL
+compdev_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ PUSB_DEV pdev;
+ LONG i;
+ ULONG dev_id;
+ PUSB_DRIVER pdrv;
+ NTSTATUS status;
+
+ if( dev_mgr == NULL || dev_handle == 0 )
+ return FALSE;
+
+ pdev = NULL;
+ dev_id = dev_handle >> 16;
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( pdev )
+ {
+ if( pdev->usb_config )
+ {
+ for( i = 0; i < pdev->usb_config->if_count; i++ )
+ {
+ if( pdrv = pdev->usb_config->interf[ i ].pif_drv )
+ {
+ pdrv->disp_tbl.dev_disconnect( dev_mgr, usb_make_handle( dev_id, i, 0 ) );
+ }
+ }
+ }
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ }
+ return TRUE;
+}
+
+// note:
+// dev_mgr_set_driver seems to be dangeous since compdev, gendrv and hub and
+// umss use it to set the driver while there may exist race condition when the
+// dev_mgr_disconnect_dev is called. If the driver is set and the
+// disconnect_dev cut in immediately, the stop or disconnect may not function
+// well. Now hub and compdev's set dev_mgr_set_driver are ok.
+//
+// another danger comes from umss's dev irp processing. This may confuse the device
+// when the disk is being read or written, and at the same time dmgrdisp's dispatch
+// route irp request to the device a control request.
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/debug.h b/reactos/drivers/usb/nt4compat/usbdriver/debug.h
new file mode 100644
index 00000000000..9bb965ca77b
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/debug.h
@@ -0,0 +1,70 @@
+#ifndef _UHCIDBG_H_
+#define _UHCIDBG_H_
+
+#define DBGLVL_OFF 0 // if gDebugLevel set to this, there is NO debug output
+#define DBGLVL_MINIMUM 1 // minimum verbosity
+#define DBGLVL_DEFAULT 2 // default verbosity level if no registry override
+#define DBGLVL_MEDIUM 3 // medium verbosity
+#define DBGLVL_HIGH 4 // highest 'safe' level (without severely affecting timing )
+#define DBGLVL_MAXIMUM 5 // maximum level, may be dangerous
+
+#ifndef DBGSTR_PREFIX
+#define DBGSTR_PREFIX "wood_uhci: "
+#endif
+
+#define DEBUG_UHCI TRUE
+#define DEBUG_HUB TRUE
+#define DEBUG_DEV_MGR TRUE
+
+#define DPRINT DbgPrint
+
+
+#define UHCI_DBGOUTSIZE 512
+
+#define hcd_dbg_print_cond( ilev, cond, _x_) \
+ if( debug_level && ( ilev <= debug_level ) && ( cond )) { \
+ DPRINT( DBGSTR_PREFIX ); \
+ DPRINT _x_ ; \
+ }
+
+#define hcd_dbg_print( ilev, _x_) hcd_dbg_print_cond( ilev, TRUE, _x_ )
+
+extern ULONG debug_level;
+
+#if DBG
+
+#define uhci_dbg_print_cond( ilev, cond, _x_ ) hcd_dbg_print_cond( ilev, cond, _x_ )
+#define uhci_dbg_print( ilev, _x_) hcd_dbg_print_cond( ilev, TRUE, _x_ )
+
+#define uhci_trap_cond( ilev, cond ) if ( debug_level && ( ilev <= debug_level ) && (cond) ) TRAP()
+#define uhci_trap( ilev ) uhci_trap_cond( ilev, TRUE )
+
+
+#define uhci_assert( cond ) ASSERT( cond )
+#define dbg_count_list( _x_ ) usb_count_list( _x_ )
+
+#define TRAP() DbgBreakPoint()
+
+#else // if not DBG
+
+// dummy definitions that go away in the retail build
+
+#define uhci_dbg_print_cond( ilev, cond, _x_ )
+#define uhci_dbg_print( ilev, _x_)
+#define uhci_trap_cont( ilev, cond )
+#define uhci_trap( ilev )
+#define uhci_assert( cond )
+#define TRAP()
+#define dbg_count_list( _x_ ) 0
+
+#endif //DBG
+
+#define usb_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ )
+#define ehci_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ )
+#define ohci_dbg_print( ilev, _x_ ) uhci_dbg_print( ilev, _x_ )
+#define ehci_dbg_print_cond( ilev, cond, _x_ ) uhci_dbg_print_cond( ilev, cond, _x_ )
+
+#define DO_NOTHING
+
+LONG usb_count_list( struct _LIST_ENTRY* list_head );
+#endif // included
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c b/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c
new file mode 100644
index 00000000000..e1395235046
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c
@@ -0,0 +1,551 @@
+/**
+ * dmgrdisp.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+#include "usb.h"
+#include "debug.h"
+#include "td.h"
+#include "hub.h"
+
+VOID
+disp_urb_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_DEV_MANAGER dev_mgr;
+ ULONG ctrl_code;
+ NTSTATUS status;
+ PDEVEXT_HEADER dev_hdr;
+
+ if( purb == NULL )
+ return;
+
+ ctrl_code = ( ULONG )purb->reference;
+ dev_mgr = ( PUSB_DEV_MANAGER )purb->context;
+
+ // at this stage, the irp can not be canceled since the urb
+ // won't be found in any queue and the irp is not in any queue.
+ // see line 4685 in hub.c
+ // Sometimes, it may be very fast to enter this routine before
+ // the dev_mgr_register_irp to be called in dispatch routine in
+ // usb2.0 environment as
+ // we did in usb1.1 driver. We can not simply add a loop to wait
+ // for the dispatch thread to add the irp to the list, because
+ // here we are at DPC level higher than the dispatch thread
+ // running level. And the solution is to register the irp
+ // before the urb is scheduled instead of registering it after
+ // urb is scheduled.
+ if( purb->pirp )
+ {
+ PIO_STACK_LOCATION irp_stack;
+ dev_mgr_remove_irp( dev_mgr, purb->pirp );
+
+ status = purb->status;
+ irp_stack = IoGetCurrentIrpStackLocation( purb->pirp );
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ purb->pirp->IoStatus.Information = 0;
+ }
+ else
+ {
+ // currently only IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
+ // are allowed. And we do not need to set information
+ // for IRP_MJ_INTERNAL_DEVICE_CONTROL
+ if( irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL )
+ purb->pirp->IoStatus.Information = purb->data_length;
+ }
+ purb->pirp->IoStatus.Status = status;
+ if( irp_stack )
+ {
+ dev_hdr = irp_stack->DeviceObject->DeviceExtension;
+ if( dev_hdr->start_io )
+ {
+ IoStartNextPacket( irp_stack->DeviceObject, TRUE );
+ }
+ }
+ IoCompleteRequest( purb->pirp, IO_NO_INCREMENT );
+ }
+ return;
+}
+
+VOID
+disp_noio_urb_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PURB purb2;
+ PUSB_DEV_MANAGER dev_mgr;
+ NTSTATUS status;
+ PIO_STACK_LOCATION irp_stack;
+ PDEVEXT_HEADER dev_hdr;
+
+ if( purb == NULL )
+ return;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ if( psetup->bmRequestType == 0x2
+ && psetup->bRequest == USB_REQ_CLEAR_FEATURE
+ && psetup->wIndex == 0 ) //reset pipe
+ purb2 = ( PURB )context;
+ else
+ purb2 = purb;
+
+ if( purb2->pirp == NULL )
+ return;
+
+ dev_mgr = ( PUSB_DEV_MANAGER )purb2->context;
+
+ dev_mgr_remove_irp( dev_mgr, purb2->pirp );
+
+ if( purb->status != STATUS_SUCCESS )
+ status = STATUS_IO_DEVICE_ERROR;
+
+ purb2->pirp->IoStatus.Information = 0;
+ purb2->pirp->IoStatus.Status = status;
+ irp_stack = IoGetCurrentIrpStackLocation( purb->pirp );
+ if( irp_stack )
+ {
+ dev_hdr = irp_stack->DeviceObject->DeviceExtension;
+ if( dev_hdr->start_io )
+ {
+ IoStartNextPacket( irp_stack->DeviceObject, TRUE );
+ }
+ }
+ IoCompleteRequest( purb2->pirp, IO_NO_INCREMENT );
+ return;
+}
+
+NTSTATUS
+dev_mgr_dispatch(
+IN PUSB_DEV_MANAGER dev_mgr,
+IN PIRP irp
+)
+//this function is called by the hcd's
+//dispatch when they have done their job.
+{
+ PIO_STACK_LOCATION irp_stack;
+ NTSTATUS status;
+ ULONG ctrl_code;
+ USE_IRQL;
+
+ if( dev_mgr == NULL || irp == NULL )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ status = STATUS_SUCCESS;
+ irp_stack = IoGetCurrentIrpStackLocation (irp);
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+
+ switch ( irp_stack->MajorFunction )
+ {
+ case IRP_MJ_CREATE:
+ {
+ InterlockedIncrement( &dev_mgr->open_count );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IRP_MJ_CLOSE:
+ {
+ InterlockedDecrement( &dev_mgr->open_count );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ case IRP_MJ_DEVICE_CONTROL:
+ {
+ switch( ctrl_code )
+ {
+ case IOCTL_GET_DEV_COUNT:
+ {
+ LONG dev_count;
+
+ irp->IoStatus.Information = 0;
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql );
+ dev_count = usb_count_list( &dev_mgr->dev_list );
+ KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql );
+
+ *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = dev_count;
+ irp->IoStatus.Information = sizeof( LONG );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_ENUM_DEVICES:
+ {
+ PLIST_ENTRY pthis, pnext;
+ LONG dev_count, array_size, i, j;
+ PUSB_DEV pdev;
+ PENUM_DEV_ARRAY peda;
+
+ irp->IoStatus.Information = 0;
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( LONG ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( ENUM_DEV_ARRAY ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ array_size = *( ( PULONG )irp->AssociatedIrp.SystemBuffer );
+
+ KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql );
+ dev_count = usb_count_list( &dev_mgr->dev_list );
+ dev_count = dev_count > array_size ? array_size : dev_count;
+ peda = ( PENUM_DEV_ARRAY )irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory( peda, sizeof( ENUM_DEV_ARRAY ) + ( dev_count - 1 ) * sizeof( ENUM_DEV_ELEMENT ) );
+
+ if( dev_count )
+ {
+ ListFirst( &dev_mgr->dev_list, pthis );
+ for( i = 0, j = 0; i < dev_count; i++ )
+ {
+ pdev = struct_ptr( pthis, USB_DEV, dev_link );
+ ListNext( &dev_mgr->dev_list, pthis, pnext );
+ pthis = pnext;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ continue;
+ }
+
+ if( dev_state( pdev ) < USB_DEV_STATE_ADDRESSED )
+ {
+ unlock_dev( pdev, FALSE );
+ continue;
+ }
+
+ peda->dev_arr[ i ].dev_handle = ( pdev->dev_id << 16 );
+ //may not get the desc yet
+ if( pdev->pusb_dev_desc )
+ {
+ peda->dev_arr[ i ].product_id = pdev->pusb_dev_desc->idProduct;
+ peda->dev_arr[ i ].vendor_id = pdev->pusb_dev_desc->idVendor;
+ }
+ else
+ {
+ peda->dev_arr[ i ].product_id = 0xffff;
+ peda->dev_arr[ i ].vendor_id = 0xffff;
+ }
+ peda->dev_arr[ i ].dev_addr = pdev->dev_addr;
+ unlock_dev( pdev, FALSE );
+ j++;
+ }
+ }
+ peda->dev_count = dev_count ? j : 0;
+ KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql );
+
+ irp->IoStatus.Information = sizeof( ENUM_DEV_ARRAY ) + ( dev_count - 1 ) * sizeof( ENUM_DEV_ELEMENT );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_GET_DEV_DESC:
+ {
+ GET_DEV_DESC_REQ gddr;
+ PUSB_DESC_HEADER pusb_desc_header;
+ KIRQL old_irql;
+ PUSB_DEV pdev;
+ LONG buf_size;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( GET_DEV_DESC_REQ ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < 8 )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ status = STATUS_SUCCESS;
+ buf_size = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
+ RtlCopyMemory( &gddr, irp->AssociatedIrp.SystemBuffer, sizeof( GET_DEV_DESC_REQ ) );
+ pusb_desc_header = irp->AssociatedIrp.SystemBuffer;
+
+ if( gddr.desc_type != USB_DT_CONFIG && gddr.desc_type != USB_DT_DEVICE )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp );
+ }
+
+ if( usb_query_and_lock_dev( dev_mgr, gddr.dev_handle, &pdev ) != STATUS_SUCCESS )
+ {
+ EXIT_DISPATCH( STATUS_IO_DEVICE_ERROR, irp );
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ goto ERROR_OUT;
+ }
+ if( dev_state( pdev ) != USB_DEV_STATE_ADDRESSED && \
+ dev_state( pdev ) != USB_DEV_STATE_CONFIGURED )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto ERROR_OUT;
+ }
+
+ if( pdev->pusb_dev_desc == NULL )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto ERROR_OUT;
+ }
+
+ if( gddr.desc_type == USB_DT_DEVICE )
+ {
+ RtlCopyMemory(
+ pusb_desc_header,
+ pdev->pusb_dev_desc,
+ buf_size > sizeof( USB_DEVICE_DESC )
+ ? sizeof( USB_DEVICE_DESC ) : buf_size );
+
+ irp->IoStatus.Information =
+ buf_size >= sizeof( USB_DEVICE_DESC )
+ ? sizeof( USB_DEVICE_DESC ): buf_size ;
+ }
+ else if( gddr.desc_type == USB_DT_CONFIG )
+ {
+ PUSB_CONFIGURATION_DESC pusb_config_desc;
+ if( pdev->pusb_dev_desc->bNumConfigurations <= gddr.desc_idx )
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto ERROR_OUT;
+ }
+
+ pusb_config_desc = usb_find_config_desc_by_idx(
+ ( PUCHAR )&pdev->pusb_dev_desc[ 1 ],
+ gddr.desc_idx,
+ pdev->pusb_dev_desc->bNumConfigurations );
+
+ if( pusb_config_desc == NULL )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto ERROR_OUT;
+ }
+
+ RtlCopyMemory(
+ pusb_desc_header,
+ pusb_config_desc,
+ buf_size >= pusb_config_desc->wTotalLength
+ ? pusb_config_desc->wTotalLength : buf_size );
+
+ irp->IoStatus.Information =
+ buf_size >= pusb_config_desc->wTotalLength
+ ? pusb_config_desc->wTotalLength : buf_size;
+ }
+ ERROR_OUT:
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ EXIT_DISPATCH( status, irp );
+ }
+ case IOCTL_SUBMIT_URB_RD:
+ case IOCTL_SUBMIT_URB_WR:
+ case IOCTL_SUBMIT_URB_NOIO:
+ {
+ LONG buf_size;
+ PURB purb;
+ KIRQL old_irql;
+ ULONG endp_idx, if_idx, user_buffer_length;
+ PUCHAR user_buffer;
+ PUSB_DEV pdev;
+ DEV_HANDLE endp_handle;
+ PUSB_ENDPOINT pendp;
+
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ purb = ( PURB )irp->AssociatedIrp.SystemBuffer;
+ endp_handle = purb->endp_handle;
+
+ if( ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR )
+ {
+ if( irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL )
+ {
+ user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
+ if( user_buffer_length == 0 )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ user_buffer = MmGetSystemAddressForMdl( irp->MdlAddress );
+ }
+ else
+ {
+ if( purb->data_buffer == NULL || purb->data_length == 0 )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ user_buffer_length = purb->data_length;
+ user_buffer = purb->data_buffer;
+ }
+ }
+
+ if( usb_query_and_lock_dev( dev_mgr, endp_handle & ~0xffff, &pdev ) != STATUS_SUCCESS )
+ {
+ EXIT_DISPATCH( STATUS_IO_DEVICE_ERROR, irp );
+ }
+
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB \
+ || ( dev_state( pdev ) < USB_DEV_STATE_ADDRESSED ) )
+
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ goto ERROR_OUT1;
+ }
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ADDRESSED
+ && !default_endp_handle( endp_handle ) )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto ERROR_OUT1;
+ }
+
+ if_idx = if_idx_from_handle( endp_handle );
+ endp_idx = endp_idx_from_handle( endp_handle );
+
+ //if_idx exceeds the upper limit
+ if( pdev->usb_config )
+ {
+ if( if_idx >= pdev->usb_config->if_count
+ || endp_idx >= pdev->usb_config->interf[ if_idx ].endp_count )
+ {
+ if( !default_endp_handle( endp_handle ) )
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ goto ERROR_OUT1;
+ }
+ }
+ }
+
+ endp_from_handle( pdev, endp_handle, pendp );
+ // FIXME: don't know what evil will let loose
+ if( endp_type( pendp ) != USB_ENDPOINT_XFER_CONTROL )
+ {
+ if( user_buffer_length > 0x100000 )
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto ERROR_OUT1;
+ }
+ }
+
+ purb->pirp = irp;
+ purb->context = dev_mgr;
+ purb->reference = ctrl_code;
+
+ if( ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR )
+ {
+ if( ctrl_code == IOCTL_SUBMIT_URB_RD )
+ KeFlushIoBuffers( irp->MdlAddress, TRUE, TRUE );
+ else
+ KeFlushIoBuffers( irp->MdlAddress, FALSE, TRUE );
+
+ purb->data_buffer = user_buffer;
+ purb->data_length = user_buffer_length;
+ purb->completion = disp_urb_completion;
+ }
+ else
+ {
+ purb->completion = disp_noio_urb_completion;
+ }
+
+ unlock_dev( pdev, FALSE );
+
+ // we have to mark irp before the urb is scheduled to
+ // avoid race condition
+ IoMarkIrpPending( irp );
+ ASSERT( dev_mgr_register_irp( dev_mgr, irp, purb ) );
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ IoGetCurrentIrpStackLocation( (irp) )->Control &= ~SL_PENDING_RETURNED;
+ dev_mgr_remove_irp( dev_mgr, irp );
+ }
+ usb_unlock_dev( pdev );
+ if( status != STATUS_PENDING)
+ {
+ irp->IoStatus.Status = status;
+ IoCompleteRequest( irp, IO_NO_INCREMENT);
+ }
+ return status;
+ ERROR_OUT1:
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ irp->IoStatus.Information = 0;
+ EXIT_DISPATCH( status, irp );
+ }
+ default:
+ {
+ irp->IoStatus.Information = 0;
+ EXIT_DISPATCH( STATUS_NOT_IMPLEMENTED, irp );
+ }
+ }
+ }
+ default:
+ {
+ irp->IoStatus.Information = 0;
+ break;
+ }
+ }
+ EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp );
+}
+
+/*#define IOCTL_GET_DEV_COUNT CTL_CODE( FILE_HCD_DEV_TYPE, 4093, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer and input_buffer_length is zero, output_buffer is to receive a dword value of the
+//dev count, output_buffer_length must be no less than sizeof( long ).
+
+#define IOCTL_ENUM_DEVICES CTL_CODE( FILE_HCD_DEV_TYPE, 4094, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer is a dword value to indicate the count of elements in the array
+//input_buffer_length is sizeof( long ), output_buffer is to receive a
+//structure ENUM_DEV_ARRAY where dev_count is the elements hold in this array.
+
+#define IOCTL_GET_DEV_DESC CTL_CODE( FILE_HCD_DEV_TYPE, 4095, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer is a structure GET_DEV_DESC_REQ, and the input_buffer_length is
+//no less than sizeof( input_buffer ), output_buffer is a buffer to receive the
+//requested dev's desc, and output_buffer_length specifies the length of the
+//buffer
+
+#define IOCTL_SUBMIT_URB_RD CTL_CODE( FILE_HCD_DEV_TYPE, 4096, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
+#define IOCTL_SUBMIT_URB_WR CTL_CODE( FILE_HCD_DEV_TYPE, 4097, METHOD_OUT_DIRECT, FILE_ANY_ACCESS )
+// input_buffer is a URB, and input_buffer_length is equal to or greater than
+// sizeof( URB ); the output_buffer is a buffer to receive data from or send data
+// to device. only the following urb fields can be accessed, others must be zeroed.
+// DEV_HANDLE endp_handle;
+// UCHAR setup_packet[8]; //for control pipe
+// the choosing of IOCTL_SUBMIT_URB_RD or IOCTL_SUBMIT_URB_WR should be determined
+// by the current URB, for example, a request string from device will use XXX_RD,
+// and a write to the bulk endpoint will use XXX_WR
+
+#define IOCTL_SUBMIT_URB_NOIO CTL_CODE( FILE_HCD_DEV_TYPE, 4098, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// input_buffer is a URB, and input_buffer_length is equal to or greater than
+// sizeof( URB ); the output_buffer is null and no output_buffer_length,
+// only the following fields in urb can be accessed, others must be zeroed.
+// DEV_HANDLE endp_handle;
+// UCHAR setup_packet[8]; //for control pipe
+*/
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.c b/reactos/drivers/usb/nt4compat/usbdriver/ehci.c
new file mode 100644
index 00000000000..e49c9944b83
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.c
@@ -0,0 +1,6733 @@
+/**
+ * ehci.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "td.h"
+#include "hub.h"
+#include "debug.h"
+#include "stdio.h"
+#include "usb.h"
+#include
+#include "ehci.h"
+//----------------------------------------------------------
+// ehci routines
+//#define DEMO
+
+#define DEFAULT_ENDP( enDP ) \
+( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP )
+
+#define dev_from_endp( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? ( ( PUSB_DEV )( enDP )->pusb_if )\
+ : ( ( enDP )->pusb_if->pusb_config->pusb_dev ) )
+
+#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK )
+
+#define endp_num( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? 0 \
+ : ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) )
+
+#define endp_dir( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? 0L\
+ : ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) )
+
+#define dev_set_state( pdEV, staTE ) \
+( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) )
+
+#define endp_max_packet_size( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \
+ ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\
+ : 8 )\
+ : ( enDP->pusb_endp_desc->wMaxPacketSize & 0x7ff ) )
+
+#define endp_mult_count( endp ) ( ( ( endp->pusb_endp_desc->wMaxPacketSize & 0x1800 ) >> 11 ) + 1 )
+
+#define release_adapter( padapTER ) \
+{\
+ ( ( padapTER ) ); \
+}
+
+#define get_int_idx( _urb, _idx ) \
+{\
+ UCHAR interVAL;\
+ interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\
+ for( _idx = 1; _idx < 9; _idx++ )\
+ {\
+ interVAL >>= 1;\
+ if( !interVAL )\
+ break;\
+ }\
+ _idx --;\
+}
+
+#define ehci_insert_urb_to_schedule( eHCI, pURB, rET ) \
+{\
+ SYNC_PARAM sync_param;\
+ sync_param.ehci = eHCI;\
+ sync_param.context = ( pURB );\
+ sync_param.ret = FALSE;\
+\
+ rET = KeSynchronizeExecution( eHCI->pdev_ext->ehci_int, ehci_sync_insert_urb_schedule, &sync_param );\
+}
+
+#define EHCI_ERROR_INT ( STS_FATAL | STS_ERR )
+#define EHCI_QH_ERROR( qh_contENT ) ( ( qh_contENT )->cur_qtd.status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) )
+#define EHCI_QTD_ERROR( qtd_contENT ) ( ( qtd_contENT )->status & ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_BABBLE | QTD_STS_XACT | QTD_STS_MMF ) )
+
+#define EHCI_READ_PORT_ULONG( pul ) ( *pul )
+#define EHCI_WRITE_PORT_ULONG( pul, src ) \
+{\
+ ULONG cmd_reg;\
+ *pul = ( ULONG )src;\
+ cmd_reg = EHCI_READ_PORT_ULONG( ehci->port_base + EHCI_USBCMD );\
+ if( cmd_reg == 0 )\
+ cmd_reg++;\
+}
+
+#define EHCI_READ_PORT_UCHAR( pch ) ( *pch )
+#define EHCI_WRITE_PORT_UCHAR( pch, src ) ( *pch = ( UCHAR )src )
+
+#define EHCI_READ_PORT_USHORT( psh ) ( *psh )
+#define EHCI_WRITE_PORT_USHORT( psh, src ) ( *psh = ( USHORT )src )
+
+#define press_doorbell( eHCI ) \
+{\
+ ULONG tmp;\
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ) );\
+ tmp |= CMD_IAAD;\
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( ( eHCI )->port_base + EHCI_USBCMD ), tmp );\
+}
+#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) )
+
+#define qh_from_list_entry( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define qtd_from_list_entry( pentry ) ( ( PEHCI_QTD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define itd_from_list_entry( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define sitd_from_list_entry( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define fstn_from_list_entry( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, elem_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+
+#define qh_from_schedule( pentry ) ( ( PEHCI_QH )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define itd_from_schedule( pentry ) ( ( PEHCI_ITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define sitd_from_schedule( pentry ) ( ( PEHCI_SITD )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+#define fstn_from_schedule( pentry ) ( ( PEHCI_FSTN )( ( ( ULONG )struct_ptr( pentry, EHCI_ELEM_LINKS, sched_link )->phys_part ) & PHYS_PART_ADDR_MASK ) )
+
+#define elem_type( ptr, from_list ) ( from_list ? ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, elem_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) \
+ : ( ( ( ( ULONG )struct_ptr( ptr, EHCI_ELEM_LINKS, sched_link)->phys_part ) & PHYS_PART_TYPE_MASK ) >> 1 ) )
+
+// #define elem_type_list_entry( pentry ) ( ( qh_from_schedule( pentry )->hw_next & 0x06 ) >> 1 )
+#define elem_type_list_entry( pentry ) ( elem_type( pentry, TRUE ) )
+
+#define get_parent_hs_hub( pDEV, parent_HUB, port_IDX ) \
+{\
+ parent_HUB = pDEV->parent_dev;\
+ port_IDX = pdev->port_idx;\
+ while( parent_HUB )\
+ {\
+ if( ( parent_HUB->flags & USB_DEV_CLASS_MASK ) != USB_DEV_CLASS_HUB )\
+ {\
+ parent_HUB = NULL;\
+ break;\
+ }\
+ if( ( parent_HUB->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )\
+ {\
+ port_IDX = parent_HUB->port_idx;\
+ parent_HUB = parent_HUB->parent_dev;\
+ continue;\
+ }\
+ break;\
+ }\
+}
+
+#define init_elem_phys_part( pelnk ) RtlZeroMemory( ( PVOID )( ( ( ULONG )( pelnk )->phys_part ) & PHYS_PART_ADDR_MASK ), get_elem_phys_part_size( ( ( ( ULONG )( pelnk )->phys_part ) & 0x06 ) >> 1 ) )
+#define REAL_INTERVAL ( 1 << pipe_content->interval )
+
+#define elem_safe_free( ptHIS, single ) \
+{\
+ UCHAR em_type; \
+ em_type = ( UCHAR )elem_type( ptHIS, TRUE ); \
+ if( ptHIS )\
+ {\
+ if( em_type == INIT_LIST_FLAG_QTD )\
+ {\
+ elem_pool_lock( qtd_pool, TRUE );\
+ if( single )\
+ elem_pool_free_elem( qtd_from_list_entry( ptHIS )->elem_head_link );\
+ else \
+ elem_pool_free_elems( qtd_from_list_entry( ptHIS )->elem_head_link );\
+ elem_pool_unlock( qtd_pool, TRUE );\
+ }\
+ else if( em_type == INIT_LIST_FLAG_ITD )\
+ {\
+ elem_pool_lock( itd_pool, TRUE );\
+ if( single )\
+ elem_pool_free_elem( itd_from_list_entry( ptHIS )->elem_head_link );\
+ else \
+ elem_pool_free_elems( itd_from_list_entry( ptHIS )->elem_head_link );\
+ elem_pool_unlock( itd_pool, TRUE );\
+ }\
+ else if( em_type == INIT_LIST_FLAG_SITD )\
+ {\
+ elem_pool_lock( sitd_pool, TRUE );\
+ if( single )\
+ elem_pool_free_elem( sitd_from_list_entry( ptHIS )->elem_head_link );\
+ else \
+ elem_pool_free_elems( sitd_from_list_entry( ptHIS )->elem_head_link );\
+ elem_pool_unlock( sitd_pool, TRUE );\
+ }\
+ else if( em_type == INIT_LIST_FLAG_FSTN )\
+ {\
+ elem_pool_lock( fstn_pool, TRUE );\
+ if( single )\
+ elem_pool_free_elem( fstn_from_list_entry( ptHIS )->elem_head_link );\
+ else \
+ elem_pool_free_elems( fstn_from_list_entry( ptHIS )->elem_head_link );\
+ elem_pool_unlock( fstn_pool, TRUE );\
+ }\
+ else if( em_type == INIT_LIST_FLAG_QH )\
+ {\
+ elem_pool_lock( qh_pool, TRUE );\
+ if( single )\
+ elem_pool_free_elem( qh_from_list_entry( ptHIS )->elem_head_link );\
+ else \
+ elem_pool_free_elems( qh_from_list_entry( ptHIS )->elem_head_link );\
+ elem_pool_unlock( qh_pool, TRUE );\
+ }\
+ }\
+}
+
+#define min( a, b ) ( ( a ) > ( b ) ? ( b ) : ( a ) )
+#define max( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
+
+#define CLR_RH2_PORTSTAT( port_idx, x ) \
+{\
+ PULONG addr; \
+ addr = ( PULONG )( ehci->port_base + port_idx ); \
+ status = EHCI_READ_PORT_ULONG( addr ); \
+ status = ( status & 0xfffffd5 ) & ~( x ); \
+ EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \
+}
+
+#define SET_RH2_PORTSTAT( port_idx, x ) \
+{\
+ PULONG addr; \
+ addr = ( PULONG )( ehci->port_base + port_idx ); \
+ status = EHCI_READ_PORT_ULONG( addr ); \
+ if( x & PORT_PR ) \
+ status = ( status & 0xffffffd1 ) | ( x ); \
+ else \
+ status = ( status & 0xffffffd5 ) | ( x ); \
+ EHCI_WRITE_PORT_ULONG( addr, ( ULONG )status ); \
+}
+
+#define ehci_from_hcd( hCD ) ( struct_ptr( ( hCD ), EHCI_DEV, hcd_interf ) )
+#define ehci_from_dev( dEV ) ( ehci_from_hcd( dEV->hcd ) )
+
+#define ehci_copy_overlay( pQHC, pTDC ) \
+{\
+ LONG td_size;\
+ PEHCI_QH pqh1;\
+ PEHCI_QTD ptd1;\
+ pqh1 = ( PEHCI_QH )( pQHC );\
+ ptd1 = ( PEHCI_QTD )( pTDC );\
+ td_size = get_elem_phys_part_size( INIT_LIST_FLAG_QTD );\
+ ( pQHC )->cur_qtd_ptr = ptd1->phys_addr;\
+ RtlZeroMemory( &( pQHC )->cur_qtd, td_size );\
+ ( pQHC )->cur_qtd.data_toggle = ( pTDC )->data_toggle;\
+ pqh1->hw_qtd_next = ptd1->phys_addr;\
+ pqh1->hw_alt_next = EHCI_PTR_TERM;\
+}
+
+//declarations
+typedef struct
+{
+ union
+ {
+ PUHCI_DEV uhci;
+ PEHCI_DEV ehci;
+ };
+ PVOID context;
+ ULONG ret;
+
+} SYNC_PARAM, *PSYNC_PARAM;
+
+PDEVICE_OBJECT
+ehci_alloc(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+ULONG bus_addr,
+PUSB_DEV_MANAGER dev_mgr
+);
+
+BOOL
+ehci_init_schedule(
+PEHCI_DEV ehci,
+PADAPTER_OBJECT padapter
+);
+
+BOOL
+ehci_release(
+PDEVICE_OBJECT pdev
+);
+
+static VOID
+ehci_stop(
+PEHCI_DEV ehci
+);
+
+BOOL
+ehci_destroy_schedule(
+PEHCI_DEV ehci
+);
+
+BOOLEAN
+ehci_sync_insert_urb_schedule(
+PVOID context
+);
+
+VOID
+ehci_init_hcd_interface(
+PEHCI_DEV ehci
+);
+
+NTSTATUS
+ehci_rh_submit_urb(
+PUSB_DEV rh,
+PURB purb
+);
+
+NTSTATUS
+ehci_dispatch_irp(
+IN PDEVICE_OBJECT DeviceObject,
+IN PIRP irp
+);
+
+VOID
+ehci_generic_urb_completion(
+PURB purb,
+PVOID context
+);
+
+static NTSTATUS
+ehci_internal_submit_bulk(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static NTSTATUS
+ehci_internal_submit_int(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static NTSTATUS
+ehci_internal_submit_ctrl(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static NTSTATUS
+ehci_internal_submit_iso(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static ULONG
+ehci_scan_iso_error(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+BOOL
+ehci_claim_bandwidth(
+PEHCI_DEV ehci,
+PURB purb,
+BOOL claim_bw
+);//true to claim band-width, false to free band-width
+
+static VOID
+ehci_insert_bulk_schedule(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+#define ehci_insert_control_schedule ehci_insert_bulk_schedule
+
+static VOID
+ehci_insert_int_schedule(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static VOID
+ehci_insert_iso_schedule(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+#define ehci_remove_control_from_schedule ehci_remove_bulk_from_schedule
+
+PDEVICE_OBJECT
+ehci_probe(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+PUSB_DEV_MANAGER dev_mgr
+);
+
+PDEVICE_OBJECT
+ehci_create_device(
+PDRIVER_OBJECT drvr_obj,
+PUSB_DEV_MANAGER dev_mgr
+);
+
+BOOL
+ehci_delete_device(
+PDEVICE_OBJECT pdev
+);
+
+VOID
+ehci_get_capabilities(
+PEHCI_DEV ehci,
+PBYTE base
+);
+
+BOOLEAN
+ehci_isr(
+PKINTERRUPT interrupt,
+PVOID context
+);
+
+BOOL
+ehci_start(
+PHCD hcd
+);
+
+extern VOID
+rh_timer_svc_reset_port_completion(
+PUSB_DEV dev,
+PVOID context
+);
+
+extern VOID
+rh_timer_svc_int_completion(
+PUSB_DEV dev,
+PVOID context
+);
+
+extern USB_DEV_MANAGER g_dev_mgr;
+
+#ifndef INCLUDE_EHCI
+ULONG debug_level = DBGLVL_MAXIMUM;
+PDRIVER_OBJECT usb_driver_obj = NULL;
+
+//pending endpoint pool funcs
+VOID
+ehci_wait_ms(
+PEHCI_DEV ehci,
+LONG ms
+)
+{
+ LARGE_INTEGER lms;
+ if( ms <= 0 )
+ return;
+
+ lms.QuadPart = -10 * ms;
+ KeSetTimer( &ehci->reset_timer, lms, NULL );
+
+ KeWaitForSingleObject(
+ &ehci->reset_timer,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ return;
+}
+
+BOOL
+init_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+)
+{
+ int i;
+ if( pool == NULL )
+ return FALSE;
+
+ pool->pending_endp_array = usb_alloc_mem( NonPagedPool, sizeof( UHCI_PENDING_ENDP ) * UHCI_MAX_PENDING_ENDPS );
+ InitializeListHead( &pool->free_que );
+ pool->free_count = 0;
+ pool->total_count = UHCI_MAX_PENDING_ENDPS;
+ KeInitializeSpinLock( &pool->pool_lock );
+
+ for( i = 0; i < MAX_TIMER_SVCS; i++ )
+ {
+ free_pending_endp( pool, &pool->pending_endp_array[i] );
+ }
+
+ return TRUE;
+
+}
+
+BOOL
+free_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+PUHCI_PENDING_ENDP pending_endp
+)
+{
+ if( pool == NULL || pending_endp == NULL )
+ {
+ return FALSE;
+ }
+
+ RtlZeroMemory( pending_endp, sizeof( UHCI_PENDING_ENDP ) );
+ InsertTailList( &pool->free_que, (PLIST_ENTRY) &pending_endp->endp_link );
+ pool->free_count++;
+
+ return TRUE;
+}
+
+PUHCI_PENDING_ENDP
+alloc_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+LONG count)
+{
+ PUHCI_PENDING_ENDP new;
+ if( pool == NULL || count != 1 )
+ return NULL;
+
+ if( pool->free_count <= 0 )
+ return NULL;
+
+ new = ( PUHCI_PENDING_ENDP )RemoveHeadList( &pool->free_que );
+ pool->free_count --;
+ return new;
+}
+
+BOOL
+destroy_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+)
+{
+ if( pool == NULL )
+ return FALSE;
+
+ InitializeListHead( &pool->free_que );
+ pool->free_count = pool->total_count = 0;
+ usb_free_mem( pool->pending_endp_array );
+ pool->pending_endp_array = NULL;
+
+ return TRUE;
+
+}
+#else
+#define ehci_wait_ms uhci_wait_ms
+extern VOID
+uhci_wait_ms(
+PEHCI_DEV ehci,
+LONG ms
+);
+
+extern BOOL
+init_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+);
+
+extern BOOL
+free_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+PUHCI_PENDING_ENDP pending_endp
+);
+
+extern PUHCI_PENDING_ENDP
+alloc_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+LONG count);
+
+extern BOOL
+destroy_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+);
+
+#endif
+
+//end of pending endpoint pool funcs
+
+static VOID
+ehci_cancel_pending_endp_urb(
+IN PVOID Parameter
+)
+{
+ PLIST_ENTRY abort_list;
+ PUSB_DEV pdev;
+ PURB purb;
+ USE_IRQL;
+
+ abort_list = ( PLIST_ENTRY )Parameter;
+
+ if( abort_list == NULL )
+ return;
+
+ while( IsListEmpty( abort_list ) == FALSE )
+ {
+ //these devs are protected by purb's ref-count
+ purb = ( PURB )RemoveHeadList( abort_list );
+ pdev = purb->pdev;
+ // purb->status is set when they are added to abort_list
+
+ ehci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, FALSE );
+ pdev->ref_count--;
+ unlock_dev( pdev, FALSE );
+ }
+ usb_free_mem( abort_list );
+ return;
+}
+
+static BOOL
+ehci_process_pending_endp(
+PEHCI_DEV ehci
+)
+{
+ PUSB_DEV pdev;
+ LIST_ENTRY temp_list, abort_list;
+ PLIST_ENTRY pthis;
+ PURB purb;
+ PUSB_ENDPOINT pendp;
+ BOOL can_submit;
+ PWORK_QUEUE_ITEM pwork_item;
+ PLIST_ENTRY cancel_list;
+ PUSB_DEV pparent;
+ UCHAR port_idx;
+ BOOL tt_needed;
+ UCHAR hub_addr;
+ PURB_HS_CONTEXT_CONTENT phcc;
+ USE_IRQL;
+
+ if( ehci == NULL )
+ return FALSE;
+
+ InitializeListHead( &temp_list );
+ InitializeListHead( &abort_list );
+
+ purb = NULL;
+ ehci_dbg_print( DBGLVL_MEDIUM, ("ehci_process_pending_endp(): entering..., ehci=0x%x\n", ehci ) );
+
+ lock_pending_endp_list( &ehci->pending_endp_list_lock );
+ while( IsListEmpty( &ehci->pending_endp_list ) == FALSE )
+ {
+
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_process_pending_endp(): pending_endp_list=0x%x\n", \
+ &ehci->pending_endp_list ) );
+
+ tt_needed = FALSE;
+ pthis = RemoveHeadList( &ehci->pending_endp_list );
+ pendp = ( ( PUHCI_PENDING_ENDP )pthis )->pendp;
+ pdev = dev_from_endp( pendp );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ //delegate to ehci_remove_device for remiving the purb queue on the endpoint
+ continue;
+ }
+ if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )
+ {
+ // prepare split transaction
+ unlock_dev( pdev, TRUE );
+
+ // pparent won't be removed when pending_endp_list_lock is acquired.
+ get_parent_hs_hub( pdev, pparent, port_idx );
+
+ if( pparent == NULL )
+ {
+ TRAP();
+ ehci_dbg_print( DBGLVL_MEDIUM, ("ehci_process_pending_endp(): full/low speed device with no parent!!!\n" ) );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ continue;
+ }
+
+ if( hub_lock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) ) == FALSE )
+ {
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) != USB_DEV_STATE_ZOMB )
+ {
+ // reinsert the pending-endp to the list
+ InsertTailList( &temp_list, pthis );
+ unlock_dev( pdev, TRUE );
+ }
+ else
+ {
+ // delegate to ehci_remove_device for purb removal
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ }
+ continue;
+ }
+
+ // backup the hub address for future use
+ hub_addr = pparent->dev_addr;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) );
+ continue;
+ }
+ tt_needed = TRUE;
+ // go on processing
+ }
+
+ if( endp_state( pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ while( IsListEmpty( &pendp->urb_list ) == FALSE )
+ {
+ purb = ( PURB )RemoveHeadList( &pendp->urb_list );
+ purb->status = USB_STATUS_ENDPOINT_HALTED;
+ InsertTailList( &abort_list, ( LIST_ENTRY* )purb );
+ }
+ InitializeListHead( &pendp->urb_list );
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ if( tt_needed )
+ hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) );
+ continue;
+ }
+
+ if( IsListEmpty( &pendp->urb_list ) == FALSE )
+ {
+ purb = ( PURB )RemoveHeadList( &pendp->urb_list );
+ ASSERT( purb );
+ }
+ else
+ {
+ InitializeListHead( &pendp->urb_list );
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ if( tt_needed )
+ hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) );
+ continue;
+ }
+
+ if( tt_needed )
+ {
+ ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr = hub_addr;
+ ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx = port_idx;
+ }
+
+ // if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule
+ switch( endp_type( pendp ) )
+ {
+ case USB_ENDPOINT_XFER_BULK:
+ {
+ can_submit = ehci_internal_submit_bulk( ehci, purb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ can_submit = ehci_internal_submit_ctrl( ehci, purb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ can_submit = ehci_internal_submit_int( ehci, purb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_ISOC:
+ {
+ can_submit = ehci_internal_submit_iso( ehci, purb );
+ break;
+ }
+ }
+
+ if( can_submit == STATUS_NO_MORE_ENTRIES )
+ {
+ //no enough bandwidth or tds
+ InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb );
+ InsertTailList( &temp_list, pthis );
+ }
+ else
+ {
+ // otherwise error or success
+ free_pending_endp(
+ &ehci->pending_endp_pool,
+ struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+
+ if( can_submit != STATUS_SUCCESS )
+ {
+ //abort these URBs
+ InsertTailList( &abort_list, ( LIST_ENTRY* )purb );
+ purb->status = can_submit;
+ }
+ }
+ unlock_dev( pdev, TRUE );
+ if( can_submit != STATUS_SUCCESS && tt_needed )
+ {
+ hub_unlock_tt( pparent, port_idx, ( UCHAR )endp_type( pendp ) );
+ }
+ }
+
+ if( IsListEmpty( &temp_list ) == FALSE )
+ {
+ //re-append them to the pending_endp_list
+ ListFirst( &temp_list, pthis );
+ RemoveEntryList( &temp_list );
+ MergeList( &ehci->pending_endp_list, pthis );
+ }
+ unlock_pending_endp_list( &ehci->pending_endp_list_lock );
+
+ if( IsListEmpty( &abort_list ) == FALSE )
+ {
+ PLIST_ENTRY pthis;
+ cancel_list = ( PLIST_ENTRY )usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( LIST_ENTRY ) );
+ ASSERT( cancel_list );
+
+ ListFirst( &abort_list, pthis );
+ RemoveEntryList( &abort_list );
+ InsertTailList( pthis, cancel_list );
+
+ pwork_item = ( PWORK_QUEUE_ITEM )&cancel_list[ 1 ];
+
+ // we do not need to worry the ehci_cancel_pending_endp_urb running when the
+ // driver is unloading since purb-reference count will prevent the dev_mgr to
+ // quit till all the reference count to the dev drop to zero.
+ ExInitializeWorkItem( pwork_item, ehci_cancel_pending_endp_urb, ( PVOID )cancel_list );
+ ExQueueWorkItem( pwork_item, DelayedWorkQueue );
+ }
+ return TRUE;
+}
+
+BOOL
+ehci_submit_urb(
+PEHCI_DEV ehci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+{
+ int i;
+ PLIST_ENTRY pthis, pnext;
+ PUHCI_PENDING_ENDP pending_endp;
+ NTSTATUS status;
+ USE_IRQL;
+
+ if( ehci == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( pdev == NULL || pendp == NULL || purb == NULL )
+ {
+ // give a chance to those pending urb, especially for clearing hub tt
+ ehci_process_pending_endp( ehci );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ lock_pending_endp_list( &ehci->pending_endp_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto LBL_OUT;
+ }
+
+ if( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB )
+ {
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &ehci->pending_endp_list_lock );
+ status = ehci_rh_submit_urb( pdev, purb );
+ return status;
+ }
+
+ if( pendp )
+ purb->pendp = pendp;
+ else
+ purb->pendp = &pdev->default_endp;
+
+ if( dev_from_endp( purb->pendp ) != pdev )
+ {
+ status = purb->status = STATUS_INVALID_PARAMETER;
+ goto LBL_OUT;
+ }
+
+ if( endp_state( purb->pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ status = purb->status = USB_STATUS_ENDPOINT_HALTED;
+ goto LBL_OUT;
+ }
+
+ if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )
+ {
+ // wait one ms
+ usb_wait_ms_dpc( 1 );
+ }
+
+ purb->pdev = pdev;
+ purb->rest_bytes = purb->data_length;
+
+ if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_BULK )
+ purb->bytes_to_transfer =
+ ( purb->data_length > EHCI_MAX_SIZE_TRANSFER ?
+ EHCI_MAX_SIZE_TRANSFER : purb->data_length ); //multiple transfer for large data block
+ else
+ purb->bytes_to_transfer = purb->data_length;
+
+ ehci_dbg_print( DBGLVL_MEDIUM, ( "ehci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer ) );
+
+ purb->bytes_transfered = 0;
+ InitializeListHead( &purb->trasac_list );
+ purb->last_finished_td = &purb->trasac_list;
+ purb->flags &= ~( URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL );
+ purb->flags |= URB_FLAG_STATE_PENDING;
+
+
+ i = IsListEmpty( &pendp->urb_list );
+ InsertTailList( &pendp->urb_list, &purb->urb_link );
+
+ pdev->ref_count ++; //for purb reference
+
+ if( i == FALSE )
+ {
+ //there is purb pending, simply queue it and return
+ status = purb->status = STATUS_PENDING;
+ goto LBL_OUT;
+ }
+ else if( usb_endp_busy_count( purb->pendp )
+ && endp_type( purb->pendp) != USB_ENDPOINT_XFER_ISOC )
+ {
+ //
+ //No purb waiting but purb overlap not allowed,
+ //so leave it in queue and return, will be scheduled
+ //later
+ //
+ status = purb->status = STATUS_PENDING;
+ goto LBL_OUT;
+ }
+
+ pending_endp = alloc_pending_endp( &ehci->pending_endp_pool, 1 );
+ if( pending_endp == NULL )
+ {
+ //panic
+ status = purb->status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT2;
+ }
+
+ pending_endp->pendp = purb->pendp;
+ InsertTailList( &ehci->pending_endp_list, ( PLIST_ENTRY )pending_endp );
+
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &ehci->pending_endp_list_lock );
+
+ ehci_process_pending_endp( ehci );
+ return STATUS_PENDING;
+
+LBL_OUT2:
+ pdev->ref_count --;
+ RemoveEntryList( ( PLIST_ENTRY )purb );
+
+LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &ehci->pending_endp_list_lock );
+ ehci_process_pending_endp( ehci );
+ return status;
+}
+
+static NTSTATUS
+ehci_set_error_code(
+PURB purb,
+ULONG raw_status
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+
+ //test if the purb is canceled
+ if( purb->flags & URB_FLAG_FORCE_CANCEL )
+ {
+ purb->status = STATUS_CANCELLED;
+ }
+ else if( raw_status == 0 )
+ purb->status = STATUS_SUCCESS;
+
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT ||
+ pipe_content->trans_type == USB_ENDPOINT_XFER_BULK ||
+ pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL )
+ {
+
+ if( raw_status & QTD_STS_BABBLE )
+ purb->status = USB_STATUS_DATA_OVERRUN;
+
+ else if( raw_status & QTD_STS_HALT )
+ purb->status = USB_STATUS_ENDPOINT_HALTED;
+
+ else if( raw_status & QTD_STS_DBE )
+ purb->status = USB_STATUS_BUFFER_OVERRUN;
+
+ else if( raw_status & QTD_STS_XACT )
+ purb->status = USB_STATUS_CRC; // crc is included in xact err.
+
+ else if( raw_status & QTD_STS_MMF )
+ purb->status = USB_STATUS_BTSTUFF;
+
+ else
+ purb->status = STATUS_UNSUCCESSFUL;
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC )
+ {
+ if( pipe_content->speed_high )
+ {
+ if( raw_status & ITD_STS_BUFERR )
+ purb->status = USB_STATUS_BUFFER_OVERRUN;
+
+ else if( raw_status & ITD_STS_BABBLE )
+ purb->status = USB_STATUS_BABBLE_DETECTED;
+
+ else if( raw_status & ITD_STS_XACTERR ) // Xact Err
+ purb->status = USB_STATUS_CRC;
+
+ else
+ purb->status = STATUS_UNSUCCESSFUL;
+
+ }
+ else
+ {
+ if( raw_status & SITD_STS_ERR ) // ERR is received from hub's tt
+ purb->status = USB_STATUS_ERROR;
+
+ else if( raw_status & SITD_STS_DBE )
+ purb->status = USB_STATUS_BUFFER_OVERRUN;
+
+ else if( raw_status & SITD_STS_BABBLE )
+ purb->status = USB_STATUS_BABBLE_DETECTED;
+
+ else if( raw_status & SITD_STS_XACTERR ) // Xact Error
+ purb->status = USB_STATUS_CRC;
+
+ else if( raw_status & SITD_STS_MISSFRM ) // missing microframe
+ purb->status = USB_STATUS_DATA_TOGGLE_MISMATCH;
+
+ else
+ purb->status = STATUS_UNSUCCESSFUL;
+ }
+ }
+ if( purb->status != STATUS_SUCCESS )
+ {
+ hcd_dbg_print( DBGLVL_MEDIUM, ( "ehci_set_error_code(): error status 0x%x\n", raw_status ) );
+ }
+ return purb->status;
+}
+
+BOOLEAN
+ehci_sync_remove_urb_finished(
+PVOID context
+)
+{
+ PEHCI_DEV ehci;
+ PLIST_ENTRY pthis, pnext, ptemp;
+ PURB purb;
+ PSYNC_PARAM pparam;
+
+ pparam = ( PSYNC_PARAM )context;
+ ehci = pparam->ehci;
+ ptemp = ( PLIST_ENTRY )pparam->context;
+
+ if( ehci == NULL )
+ {
+ return ( UCHAR )pparam->ret = FALSE;
+ }
+
+ ListFirst( &ehci->urb_list, pthis );
+ while( pthis )
+ {
+ //remove urbs not in the schedule
+ ListNext( &ehci->urb_list, pthis, pnext );
+ purb = ( PURB )pthis;
+
+ if( ( purb->flags & URB_FLAG_IN_SCHEDULE ) == 0 )
+ {
+ //finished or canceled( not applied for split bulk ).
+ RemoveEntryList( pthis );
+ InsertTailList( ptemp, pthis );
+ }
+ pthis = pnext;
+ }
+ pparam->ret = TRUE;
+ return ( UCHAR )TRUE;
+}
+
+VOID
+ehci_dpc_callback(
+PKDPC dpc,
+PVOID context,
+PVOID sysarg1,
+PVOID sysarg2
+)
+{
+ PEHCI_DEV ehci;
+
+ LIST_HEAD temp_list;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ PEHCI_QH pqh;
+ PEHCI_QTD ptd;
+ PUHCI_PENDING_ENDP pending_endp;
+ PEHCI_FSTN pfstn;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+
+ BOOL finished;
+ LONG i;
+ ULONG ehci_status, urb_status, toggle = 0;
+
+ SYNC_PARAM sync_param;
+ UCHAR ep_type;
+ USE_IRQL;
+
+ ehci = ( PEHCI_DEV ) context;
+ if( ehci == NULL )
+ return;
+
+ ehci_status = ( ULONG )sysarg1;
+
+ InitializeListHead( &temp_list );
+
+ sync_param.ehci = ehci;
+ sync_param.context = ( PVOID )&temp_list;
+
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_dpc_callback(): entering..., ehci=0x%x\n", ehci ) );
+ //remove finished purb from ehci's purb-list
+ KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_remove_urb_finished, &sync_param );
+
+ //release resources( itds, sitds, fstns, tds, and qhs ) allocated for the purb
+ while( IsListEmpty( &temp_list ) == FALSE )
+ {
+ //not in any public queue, if do not access into dev, no race
+ //condition will occur
+ purb = ( PURB ) RemoveHeadList( &temp_list );
+ urb_status = purb->status;
+ ep_type = endp_type( purb->pendp );
+
+ if( ep_type == USB_ENDPOINT_XFER_ISOC )
+ {
+ // collect error for iso transfer
+ urb_status = ehci_scan_iso_error( ehci, purb );
+ }
+
+ //the only place we do not use this lock on non-pending-endp-list data
+ KeAcquireSpinLockAtDpcLevel( &ehci->pending_endp_list_lock );
+ while( IsListEmpty( &purb->trasac_list ) == FALSE )
+ {
+ UCHAR em_type;
+ pthis = RemoveHeadList( &purb->trasac_list );
+ em_type = ( UCHAR )elem_type( pthis, TRUE );
+
+ if( em_type == INIT_LIST_FLAG_QH )
+ {
+ pqh = qh_from_list_entry( pthis );
+ elem_safe_free( pthis, TRUE );
+ }
+ else
+ {
+ //must be an itd, sitd chain
+ InsertHeadList( &purb->trasac_list, pthis );
+ for( i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++ )
+ {
+ PEHCI_QTD_CONTENT ptdc;
+ PEHCI_ITD_CONTENT pitdc;
+ PEHCI_SITD_CONTENT psitdc;
+
+ em_type = ( UCHAR )elem_type( pthis, TRUE );
+
+ // accumulate data transfered in tds
+ if( em_type == INIT_LIST_FLAG_QTD )
+ {
+ ptd = qtd_from_list_entry( pthis );
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+ if( ( ptdc->status & QTD_STS_ACTIVE ) == 0 && ( ( ptdc->status & QTD_ANY_ERROR ) == 0 ) )
+ purb->bytes_transfered += ptd->bytes_to_transfer;
+ }
+ else if( em_type == INIT_LIST_FLAG_ITD )
+ {
+ int j;
+ pitdc = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis );
+ for( j = 0; j < 8; j++ )
+ {
+ if( ( pitdc->status_slot[ j ].status & ITD_STS_ACTIVE ) == 0 && ( pitdc->status_slot[ j ].status & ITD_ANY_ERROR ) == 0 )
+ purb->bytes_transfered += ptdc->bytes_to_transfer;
+ }
+ }
+ else if( em_type == INIT_LIST_FLAG_SITD )
+ {
+ psitdc = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis );
+ if( ( psitdc->status & SITD_STS_ACTIVE ) == 0 && ( psitdc->status & SITD_ANY_ERROR ) == 0 )
+ purb->bytes_transfered += ptdc->bytes_to_transfer;
+ }
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ // check to see if an fstn is there
+ ListFirstPrev( &purb->trasac_list, pthis );
+ if( elem_type( pthis, TRUE ) == INIT_LIST_FLAG_FSTN )
+ {
+ RemoveEntryList( pthis );
+ elem_safe_free( pthis, TRUE );
+ }
+
+ ListFirst( &purb->trasac_list, pthis );
+ RemoveEntryList( &purb->trasac_list );
+
+ // free the tds
+ elem_safe_free( pthis, FALSE );
+
+ //termination condition
+ InitializeListHead( &purb->trasac_list );
+ purb->last_finished_td = NULL;
+ }
+ }
+
+ if( ep_type == USB_ENDPOINT_XFER_ISOC
+ || ep_type == USB_ENDPOINT_XFER_INT )
+ ehci_claim_bandwidth( ehci, purb, FALSE); //release band-width
+
+ KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock );
+
+ ehci_set_error_code( purb, urb_status );
+
+ pdev = dev_from_endp( purb->pendp );
+ pendp = purb->pendp;
+
+ // perform clear tt buffer if error on full/low bulk/control pipe
+ if( ep_type == USB_ENDPOINT_XFER_BULK || ep_type == USB_ENDPOINT_XFER_CONTROL )
+ {
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PUSB_DEV phub;
+ UCHAR port_idx;
+
+ get_parent_hs_hub( pdev, phub, port_idx );
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+
+ if( pipe_content->speed_high == 0 && purb->status != STATUS_SUCCESS )
+ {
+ // lets schedule an event to clear the tt buffer
+ hub_post_clear_tt_event( phub, port_idx, purb->pipe );
+ }
+ else if( pipe_content->speed_high == 0 )
+ {
+ if( phub == NULL )
+ TRAP();
+ else
+ {
+ // release tt if no error
+ hub_unlock_tt( phub, ( UCHAR )port_idx, ( UCHAR )pipe_content->trans_type );
+ }
+ }
+ }
+
+ finished = TRUE;
+
+ //since the ref_count for the purb is not released, we can safely have one
+ //pointer to dev
+
+ if( purb->status == USB_STATUS_BABBLE_DETECTED )
+ {
+ usb_dbg_print( DBGLVL_MEDIUM, ( "ehci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n" ) );
+ // ehci_start( ehci );
+ }
+
+ if ( ehci_status & STS_HALT ) //&& !ehci->is_suspended
+ {
+ ehci_start( &ehci->hcd_interf );
+ }
+
+ //this will let the new request in ehci_generic_urb_completion to this endp
+ //be processed rather than queued in the pending_endp_list
+ lock_dev( pdev, TRUE );
+ usb_endp_busy_count_dec( pendp );
+ unlock_dev( pdev, TRUE );
+
+ if( usb_success( purb->status ) == FALSE )
+ {
+ // set error code and complete the purb and purb is invalid from this point
+ ehci_generic_urb_completion( purb, purb->context );
+ }
+ else
+ {
+ if( ep_type == USB_ENDPOINT_XFER_BULK )
+ {
+ purb->rest_bytes -= purb->bytes_transfered;
+ if( purb->rest_bytes )
+ {
+ finished = FALSE;
+ }
+ else
+ {
+ ehci_generic_urb_completion( purb, purb->context );
+ }
+ }
+ else
+ {
+ ehci_generic_urb_completion( purb, purb->context );
+ // DbgBreakPoint();
+ //purb is now invalid
+ }
+ }
+
+ KeAcquireSpinLockAtDpcLevel( &ehci->pending_endp_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( finished )
+ pdev->ref_count--;
+
+ if( urb_status &&
+ ( ( ep_type == USB_ENDPOINT_XFER_BULK ) ||
+ ( ep_type == USB_ENDPOINT_XFER_INT ) ) )
+ {
+ // error on int or bulk pipe, cleared in usb_reset_pipe_completion
+ pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
+ pendp->flags |= USB_ENDP_FLAG_STALL;
+ }
+
+ if( dev_state( pdev )== USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock );
+ if( finished == FALSE )
+ {
+
+ purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ehci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, TRUE );
+ pdev->ref_count--;
+ unlock_dev( pdev, TRUE);
+ }
+ continue;
+ }
+
+ if( finished && IsListEmpty( &pendp->urb_list ) == TRUE )
+ {
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock );
+ continue;
+ }
+ else if( finished == TRUE )
+ {
+ //has purb in the endp's purb-list
+ if( usb_endp_busy_count( pendp ) > 0 )
+ {
+ //the urbs still have chance to be sheduled but not this time
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock );
+ continue;
+ }
+ }
+
+ if( finished == FALSE )
+ {
+ //a split bulk transfer, ( not the high speed split transfer )
+ purb->bytes_transfered = 0;
+ purb->bytes_to_transfer =
+ EHCI_MAX_SIZE_TRANSFER > purb->rest_bytes
+ ? purb->rest_bytes
+ : EHCI_MAX_SIZE_TRANSFER;
+
+ //the purb is not finished
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_PENDING;
+
+ InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb );
+ }
+
+ pending_endp = alloc_pending_endp( &ehci->pending_endp_pool, 1 );
+ pending_endp->pendp = pendp;
+ InsertTailList( &ehci->pending_endp_list, &pending_endp->endp_link );
+
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &ehci->pending_endp_list_lock );
+ }
+
+ //ah...exhausted, let's find some in the pending_endp_list to rock
+ ehci_process_pending_endp( ehci );
+ return;
+}
+
+BOOLEAN
+ehci_sync_cancel_urbs_dev(
+PVOID context
+)
+{
+ //cancel all the urbs on one dev
+ PEHCI_DEV ehci;
+ PUSB_DEV pdev, dest_dev;
+ PSYNC_PARAM sync_param;
+ PLIST_ENTRY pthis, pnext;
+ LONG count;
+
+ sync_param = ( PSYNC_PARAM )context;
+ dest_dev = ( PUSB_DEV )sync_param->context;
+ ehci = sync_param->ehci;
+
+ if( ehci == NULL || dest_dev == NULL )
+ {
+ return ( UCHAR )sync_param->ret = FALSE;
+ }
+ count = 0;
+ ListFirst( &ehci->urb_list, pthis );
+ while( pthis )
+ {
+ pdev = dev_from_endp( ( ( PURB ) pthis )->pendp );
+ if( pdev == dest_dev )
+ {
+ ( ( PURB ) pthis )->flags |= URB_FLAG_FORCE_CANCEL;
+ }
+ ListNext( &ehci->urb_list, pthis, pnext );
+ pthis = pnext;
+ count ++;
+ }
+
+ if( count )
+ {
+ // signal an int for further process
+ press_doorbell( ehci );
+ }
+ return ( UCHAR )sync_param->ret = TRUE;
+}
+
+BOOL
+ehci_remove_device(
+PEHCI_DEV ehci,
+PUSB_DEV dev
+)
+{
+ PUHCI_PENDING_ENDP ppending_endp;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ LIST_HEAD temp_list;
+ int i, j, k;
+ SYNC_PARAM sync_param;
+
+ USE_IRQL;
+
+ if( ehci == NULL || dev == NULL )
+ return FALSE;
+
+ InitializeListHead( &temp_list );
+
+ //free pending endp that has purb queued from pending endp list
+ lock_pending_endp_list( &ehci->pending_endp_list_lock );
+
+ ListFirst( &ehci->pending_endp_list, pthis );
+
+ while( pthis )
+ {
+ ppending_endp = ( PUHCI_PENDING_ENDP ) pthis;
+ ListNext( &ehci->pending_endp_list, pthis, pnext );
+ if( dev_from_endp( ppending_endp->pendp ) == dev )
+ {
+ RemoveEntryList( pthis );
+ free_pending_endp( &ehci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ }
+ pthis = pnext;
+ }
+ unlock_pending_endp_list( &ehci->pending_endp_list_lock );
+
+ //cancel all the urbs in the purb-list
+ sync_param.ehci = ehci;
+ sync_param.context = ( PVOID )dev;
+
+ KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_cancel_urbs_dev, &sync_param );
+
+ //cancel all the purb in the endp's purb-list
+ k = 0;
+ lock_dev( dev, FALSE );
+ if( dev->usb_config )
+ {
+ //only for configed dev
+ for( i = 0; i < dev->usb_config->if_count; i++ )
+ {
+ for( j = 0; j < dev->usb_config->interf[ i ].endp_count; j++ )
+ {
+ ListFirst( &dev->usb_config->interf[ i ].endp[ j ].urb_list, pthis );
+ while( pthis )
+ {
+ ListNext( &dev->usb_config->interf[ i ].endp[ j ].urb_list,
+ pthis,
+ pnext );
+
+ RemoveEntryList( pthis );
+ InsertHeadList( &temp_list, pthis );
+ pthis = pnext;
+ k++;
+ }
+
+ }
+ }
+ }
+ ListFirst( &dev->default_endp.urb_list, pthis );
+
+ while( pthis )
+ {
+ ListNext( &dev->default_endp.urb_list,
+ pthis,
+ pnext );
+
+ RemoveEntryList( pthis );
+ InsertHeadList( &temp_list, pthis );
+ pthis = pnext;
+ k++;
+ }
+ unlock_dev( dev, FALSE );
+
+ if( IsListEmpty( &temp_list ) == FALSE )
+ {
+ for( i = 0; i < k ; i++ )
+ {
+ //complete those urbs with error
+ pthis = RemoveHeadList( &temp_list );
+ purb = ( PURB ) pthis;
+ purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ {
+ ehci_generic_urb_completion( purb, purb->context );
+ }
+ }
+ }
+
+ lock_dev( dev, FALSE )
+ dev->ref_count -= k;
+ unlock_dev( dev, FALSE );
+
+ return TRUE;
+}
+
+static BOOL
+ehci_insert_urb_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+// must have dev_lock( ehci_process_pending_endp ) and frame_list_lock acquired
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+
+ if( ehci == NULL || purb == NULL )
+ return FALSE;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ switch( pipe_content->trans_type )
+ {
+ case USB_ENDPOINT_XFER_CONTROL:
+ ehci_insert_control_schedule( ehci, purb );
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ ehci_insert_bulk_schedule( ehci, purb );
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ ehci_insert_int_schedule( ehci, purb );
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ ehci_insert_iso_schedule( ehci, purb );
+ break;
+ default:
+ return FALSE;
+ }
+
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE;
+ InsertTailList( &ehci->urb_list, ( PLIST_ENTRY )purb );
+
+ return TRUE;
+}
+
+static BOOL
+ehci_insert_tds_qh(
+PEHCI_DEV ehci,
+PEHCI_QH pqh,
+PEHCI_QTD td_chain
+)
+{
+ if( pqh == NULL || td_chain == NULL )
+ return FALSE;
+
+ ehci_copy_overlay( ( PEHCI_QH_CONTENT )pqh, ( PEHCI_QTD_CONTENT )td_chain );
+ InsertTailList( &td_chain->elem_head_link->elem_link, &pqh->elem_head_link->elem_link );
+ return TRUE;
+ ehci;
+}
+
+static BOOL
+ehci_insert_qh_urb(
+PURB purb,
+PEHCI_QH pqh
+)
+{
+ PLIST_ENTRY pthis, pnext;
+ if( pqh == NULL || purb == NULL )
+ return FALSE;
+
+ InsertTailList( &pqh->elem_head_link->elem_link, &purb->trasac_list );
+ ListFirst( &purb->trasac_list, pthis )
+ while( pthis )
+ {
+ // note: fstn may in this chain
+ struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->purb = purb;
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+ return TRUE;
+}
+
+#define calc_td_count( pURB, start_aDDR, td_coUNT ) \
+{\
+ LONG i, j, k;\
+ td_coUNT = 0;\
+ k = ( ( pURB )->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size;\
+ if( k != 0 )\
+ {\
+ LONG packets_per_td, packets_per_page;\
+ packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size;\
+ packets_per_page = PAGE_SIZE / max_packet_size;\
+ i = ( ( LONG )&( pURB )->data_buffer[ ( start_aDDR ) ] ) & ( PAGE_SIZE - 1 );\
+ if( i )\
+ {\
+ i = PAGE_SIZE - i;\
+ j = i & ( max_packet_size - 1 );\
+ k -= ( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) / max_packet_size;\
+ if( k < 0 )\
+ td_coUNT = 1;\
+ else\
+ {\
+ if( j )\
+ i = packets_per_td - packets_per_page;\
+ else\
+ i = packets_per_td;\
+ td_coUNT = 1 + ( k + i - 1 ) / i; \
+ }\
+ }\
+ else\
+ {\
+ td_coUNT = ( k + packets_per_td - 1 ) / packets_per_td;\
+ }\
+ }\
+}
+
+static BOOL
+ehci_fill_td_buf_ptr(
+PURB purb,
+LONG start_addr, // start idx into purb->data_buffer
+PLIST_ENTRY td_list,
+LONG td_count,
+ULONG toggle
+)
+// fill the tds' bytes_to_transfer and hw_buf, return next toggle value: true 1, false 0
+{
+ LONG i, j, k, data_load;
+ LONG packets_per_td, packets_per_page, bytes_to_transfer, max_packet_size;
+ PLIST_ENTRY pthis, pnext;
+ PEHCI_QTD_CONTENT ptdc;
+ PEHCI_QTD ptd;
+ PVOID ptr;
+
+ if( purb == NULL || td_list == NULL || td_count == 0 )
+ return toggle;
+
+ max_packet_size = 1 << ( ( PURB_HS_PIPE_CONTENT )&purb->pipe )->max_packet_size;
+ packets_per_td = EHCI_QTD_MAX_TRANS_SIZE / max_packet_size;
+ packets_per_page = PAGE_SIZE / max_packet_size;
+
+ pthis = td_list;
+ bytes_to_transfer = purb->bytes_to_transfer;
+
+ i = ( ( LONG )&( purb )->data_buffer[ ( start_addr ) ] ) & ( PAGE_SIZE - 1 );
+ if( i )
+ {
+ i = PAGE_SIZE - i;
+ j = i & ( max_packet_size - 1 );
+ }
+ else
+ {
+ i = j = 0;
+ }
+
+ while( bytes_to_transfer )
+ {
+ ptd = qtd_from_list_entry( pthis );
+ ptd->hw_buf[ 0 ] = MmGetPhysicalAddress( &purb->data_buffer[ start_addr ] ).LowPart;
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+
+ if( i != 0 )
+ {
+ data_load = ( LONG )( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) < bytes_to_transfer
+ ? ( LONG )( EHCI_QTD_MAX_TRANS_SIZE - PAGE_SIZE + i - j ) : bytes_to_transfer;
+
+ ptdc->bytes_to_transfer = ( USHORT )data_load;
+ ptd->bytes_to_transfer = ( USHORT )data_load;
+
+ // subtract the header part
+ data_load -= ( i < data_load ? i : data_load );
+
+ for( k = 1; data_load > 0; k++ )
+ {
+ ptr = &purb->data_buffer[ start_addr + i + ( k - 1 ) * PAGE_SIZE ];
+ ptr = ( PVOID )( ( ( ULONG )ptr ) & ~( PAGE_SIZE - 1 ) );
+ ptd->hw_buf[ k ] = MmGetPhysicalAddress( ptr ).LowPart;
+ data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load;
+ }
+ }
+ else
+ {
+ // aligned on page boundary
+ data_load = EHCI_QTD_MAX_TRANS_SIZE < bytes_to_transfer
+ ? EHCI_QTD_MAX_TRANS_SIZE : bytes_to_transfer;
+
+ ptdc->bytes_to_transfer = ( USHORT )data_load;
+ ptd->bytes_to_transfer = ( USHORT )data_load;
+
+ data_load -= ( PAGE_SIZE < data_load ? PAGE_SIZE : data_load );
+
+ for( k = 1; data_load > 0; k++ )
+ {
+ ptr = &purb->data_buffer[ start_addr + k * PAGE_SIZE ];
+ ptr = ( PVOID )( ( ( ULONG )ptr ) & ~( PAGE_SIZE - 1 ) );
+ ptd->hw_buf[ k ] = MmGetPhysicalAddress( ptr ).LowPart;
+ data_load -= PAGE_SIZE < data_load ? PAGE_SIZE : data_load;
+ }
+ }
+ ptdc->data_toggle = toggle;
+ if( ( ( ptdc->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size ) & 1 )
+ {
+ //only odd num of transactions has effect
+ toggle ^= 1;
+ }
+ start_addr += ptdc->bytes_to_transfer;
+ bytes_to_transfer -= ptdc->bytes_to_transfer;
+ ListNext( td_list, pthis, pnext );
+ pthis = pnext;
+ i = j;
+ }
+ return toggle;
+}
+
+static NTSTATUS
+ehci_internal_submit_bulk(
+PEHCI_DEV ehci,
+PURB purb
+)
+//
+// assume that the purb has its rest_bytes and bytes_to_transfer set
+// and bytes_transfered is zeroed.
+// dev_lock must be acquired outside
+// purb comes from dev's endpoint purb-list. it is already removed from
+// the endpoint purb-list.
+//
+{
+
+ LONG max_packet_size, td_count, offset, bytes_to_transfer, data_load;
+ PBYTE start_addr;
+ PEHCI_QTD ptd;
+ PEHCI_QH pqh;
+ LIST_ENTRY td_list, *pthis, *pnext;
+ BOOL old_toggle, toggle, ret;
+ UCHAR pid;
+ LONG i, j, k;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PEHCI_QTD_CONTENT ptdc;
+ PEHCI_QH_CONTENT pqhc;
+ PEHCI_ELEM_LINKS pelnk;
+
+ if( ehci == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ max_packet_size = endp_max_packet_size( purb->pendp );
+ if( purb->bytes_to_transfer == 0 )
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ start_addr = &purb->data_buffer[ purb->data_length - purb->rest_bytes ];
+ calc_td_count( purb, purb->data_length - purb->rest_bytes, td_count );
+
+ elem_pool_lock( qtd_pool , TRUE );
+ pelnk = elem_pool_alloc_elems( qtd_pool, td_count );
+ elem_pool_unlock( qtd_pool, TRUE );
+
+ if( pelnk == NULL )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+ ptd = ( PEHCI_QTD )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+
+ InitializeListHead( &td_list );
+ InsertTailList( &ptd->elem_head_link->elem_link, &td_list );
+
+ ListFirst( &td_list, pthis );
+ ListNext( &td_list, pthis, pnext );
+
+ offset = 0;
+
+ old_toggle = toggle = ( purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE;
+ bytes_to_transfer = purb->bytes_to_transfer;
+ ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_internal_submit_bulk():dev toggle=%d\n", toggle ) );
+
+ for( i = 1; i < 16; i++ )
+ {
+ if( ( max_packet_size >> i ) == 0 )
+ break;
+ }
+ i--;
+ i &= 0xf;
+
+ purb->pipe = 0;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ pipe_content->max_packet_size = i;
+ pipe_content->endp_addr = endp_num( purb->pendp );
+ pipe_content->dev_addr = dev_from_endp( purb->pendp )->dev_addr;
+ pipe_content->trans_dir = endp_dir( purb->pendp );
+ pipe_content->trans_type = USB_ENDPOINT_XFER_BULK;
+ pipe_content->data_toggle = toggle;
+ pipe_content->speed_high = ( dev_from_endp( purb->pendp )->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0;
+ pipe_content->speed_low = ( dev_from_endp( purb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0;
+
+ pid = ( ( ( ULONG )purb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ? QTD_PID_IN : QTD_PID_OUT );
+
+ i = ( ( ULONG )start_addr ) & ( PAGE_SIZE - 1 ) ; // header part within first page
+ if( i )
+ {
+ i = PAGE_SIZE - i;
+ if( i < purb->bytes_to_transfer )
+ j = i & ( max_packet_size - 1 );
+ else
+ j = 0;
+ }
+ else
+ j = 0;
+
+ // fill the page pointer and toggle
+
+ toggle = ehci_fill_td_buf_ptr( purb, purb->data_length - purb->rest_bytes, pthis, td_count, toggle );
+ while( pthis )
+ {
+ ptd = qtd_from_list_entry( pthis );
+ ptdc = ( PEHCI_QTD_CONTENT ) ptd;
+
+ // ptdc->alt_terminal = 1;
+ // ptdc->alt_qtd = 0;
+ ptd->hw_alt_next = EHCI_PTR_TERM;
+ ptdc->pid = pid;
+
+ // ptd->elem_head_link->purb = purb; will be filled later
+ ptdc->err_count = 3;
+ ptdc->status = 0x80; // active, and do_start_split for split transfer
+ ptdc->cur_page = 0;
+ // ptdc->data_toggle = toggle;
+
+ if( pnext )
+ {
+ ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr;
+ }
+ else
+ {
+ //Last one, enable ioc and short packet detect if necessary
+ ptd->hw_next = EHCI_PTR_TERM;
+ ptdc->ioc = TRUE;
+ if( bytes_to_transfer < max_packet_size && ( pid == QTD_PID_IN ) )
+ {
+ //ptd->status |= TD_CTRL_SPD;
+ }
+ }
+
+ pthis = pnext;
+
+ if( pthis )
+ ListNext( &td_list, pthis, pnext );
+ }
+
+ ListFirst( &td_list, pthis );
+ RemoveEntryList( &td_list );
+
+ elem_pool_lock( qh_pool, TRUE );
+ pqh = ( PEHCI_QH )( ( ULONG )elem_pool_alloc_elem( qh_pool )->phys_part & PHYS_PART_ADDR_MASK );
+ elem_pool_unlock( qh_pool, TRUE);
+
+ if( pqh == NULL )
+ {
+ // free the qtds
+ elem_safe_free( pthis, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+
+ purb->td_count = td_count;
+ pqhc = ( PEHCI_QH_CONTENT )pqh;
+ pqh->hw_next = EHCI_PTR_TERM; // filled later
+ pqhc->dev_addr = pipe_content->dev_addr;
+ pqhc->inactive = 0;
+ pqhc->endp_addr = pipe_content->endp_addr;
+ pqhc->data_toggle = 0; //pipe_content->data_toggle;
+ pqhc->is_async_head = 0;
+ pqhc->max_packet_size = ( 1 << pipe_content->max_packet_size );
+ pqhc->is_ctrl_endp = 0;
+ pqhc->reload_counter = EHCI_NAK_RL_COUNT;
+
+ if( pipe_content->speed_high )
+ pqhc->endp_spd = USB_SPEED_HIGH;
+ else if( pipe_content->speed_low )
+ pqhc->endp_spd = USB_SPEED_LOW;
+ else
+ pqhc->endp_spd = USB_SPEED_FULL;
+
+ pqh->hw_info2 = 0;
+ pqhc->mult = 1;
+ pqh->hw_current = 0;
+ pqh->hw_qtd_next = 0; // filled later
+ pqh->hw_alt_next = EHCI_PTR_TERM;
+ pqh->hw_token = 0; //indicate to advance queue before execution
+
+ if( !pipe_content->speed_high )
+ {
+ pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr;
+ pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx;
+ }
+
+ ptd = qtd_from_list_entry( pthis );
+ ehci_insert_tds_qh( ehci, pqh, ptd );
+ ehci_insert_qh_urb( purb, pqh );
+ purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ;
+ usb_endp_busy_count_inc( purb->pendp );
+ ehci_insert_urb_to_schedule( ehci, purb, ret );
+
+ if( ret == FALSE )
+ {
+ // undo all we have done
+ ListFirst( &pqh->elem_head_link->elem_link, pthis );
+
+ RemoveEntryList( &purb->trasac_list );
+ RemoveEntryList( &pqh->elem_head_link->elem_link ); //remove qh from td_chain
+
+ elem_safe_free( pthis, FALSE );
+ elem_safe_free( &pqh->elem_head_link->elem_link, TRUE );
+
+ InitializeListHead( &purb->trasac_list );
+ // usb_endp_busy_count_dec( purb->pendp ); // the decrement is done in the dpc callback
+ purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ;
+ return STATUS_UNSUCCESSFUL;
+ }
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+ehci_internal_submit_ctrl(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+
+ LIST_ENTRY td_list, *pthis, *pnext;
+ LONG i, j, td_count;
+ LONG toggle;
+ LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx;
+
+ PEHCI_QTD ptd;
+ PEHCI_QH pqh;
+ PEHCI_QH_CONTENT pqhc;
+ UCHAR dev_addr;
+ BOOL ret;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PEHCI_QTD_CONTENT ptdc;
+ PEHCI_ELEM_LINKS pelnk;
+ PUSB_DEV pdev;
+
+ if( ehci == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ bytes_rest = purb->rest_bytes;
+ bytes_to_transfer = purb->bytes_to_transfer;
+ max_packet_size = endp_max_packet_size( purb->pendp );
+ start_idx = purb->data_length - purb->rest_bytes;
+
+ calc_td_count( purb, start_idx, td_count );
+ td_count += 2; // add setup td and handshake td
+
+ elem_pool_lock( qtd_pool, TRUE );
+ pelnk = elem_pool_alloc_elems( qtd_pool, td_count );
+ elem_pool_unlock( qtd_pool, TRUE );
+
+ if( pelnk == NULL )
+ {
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ InsertTailList( &pelnk->elem_link, &td_list );
+ ListFirst( &td_list, pthis );
+ ListNext( &td_list, pthis, pnext );
+
+ ptd = qtd_from_list_entry( pthis );
+
+ pdev = dev_from_endp( purb->pendp );
+ dev_addr = pdev->dev_addr;
+
+ if( dev_state( pdev ) <= USB_DEV_STATE_RESET ) //only valid for control transfer
+ dev_addr = 0;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_internal_submit_ctrl(): dev_addr =0x%x\n", \
+ dev_addr ) );
+
+ // fill the setup packet
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+ ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr;
+ ptd->hw_alt_next = EHCI_PTR_TERM;
+ ptdc->status = 0x80; // active
+ ptdc->pid = QTD_PID_SETUP;
+ ptdc->err_count = 3;
+ ptdc->cur_page = 0;
+ ptdc->ioc = 0;
+ ptdc->bytes_to_transfer = sizeof( USB_CTRL_SETUP_PACKET );
+ ptdc->data_toggle = 0;
+ ptd->hw_buf[ 0 ] = MmGetPhysicalAddress( purb->setup_packet ).LowPart;
+
+ for( i = 1; i < 16; i++ )
+ {
+ if( ( max_packet_size >> i ) == 0 )
+ break;
+ }
+ i--;
+ i &= 0xf;
+
+ purb->pipe = 0;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ pipe_content->max_packet_size = i;
+ pipe_content->endp_addr = endp_num( purb->pendp );
+ pipe_content->dev_addr = dev_addr;
+ pipe_content->speed_low = ( pdev->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0;
+ pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0;
+ pipe_content->trans_type = USB_ENDPOINT_XFER_CONTROL;
+
+ pthis = pnext;
+ ListNext( &td_list, pthis, pnext );
+
+ // all the tds's toggle and data_buffer pointer is filled here
+ toggle = 1;
+ ehci_fill_td_buf_ptr( purb, start_idx, pthis, td_count - 2, toggle );
+
+ for( i = 0; ( ( i < td_count - 2 ) && pthis ); i++ )
+ {
+ //construct tds for DATA packets of data stage.
+ ptd = qtd_from_list_entry( pthis );
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+ ptd->hw_alt_next = EHCI_PTR_TERM;
+ ptdc->status = 0x80; // active and startXSplit
+ ptdc->pid = ( ( purb->setup_packet[ 0 ] & USB_DIR_IN ) ? QTD_PID_IN : QTD_PID_OUT );
+ ptdc->err_count = 3;
+ ptdc->cur_page = 0;
+ ptdc->ioc = 0;
+
+ if( pnext )
+ ptd->hw_next = qtd_from_list_entry( pnext )->phys_addr;
+ else
+ ptd->hw_next = EHCI_PTR_TERM;
+
+ pthis = pnext;
+ if( pthis )
+ ListNext( &td_list, pthis, pnext );
+ }
+
+ if( pthis )
+ ptd->hw_next = qtd_from_list_entry( pthis )->phys_addr;
+ else
+ TRAP();
+
+ // ListFirstPrev( &td_list, pthis );
+ ptd = qtd_from_list_entry( pthis );
+
+ //the last is an IN transaction
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+ ptd->hw_alt_next = EHCI_PTR_TERM;
+ ptdc->status = 0x80;
+ ptdc->pid = ( ( td_count > 2 )
+ ? ( ( purb->setup_packet[ 0 ] & USB_DIR_IN )
+ ? QTD_PID_OUT : QTD_PID_IN )
+ : QTD_PID_IN );
+
+ ptdc->err_count = 3;
+ ptdc->cur_page = 0;
+ ptdc->ioc = 1;
+ ptdc->bytes_to_transfer = 0;
+ ptdc->data_toggle = 1;
+ ptd->hw_next = EHCI_PTR_TERM;
+
+ ListFirst( &td_list, pthis );
+ RemoveEntryList( &td_list );
+
+ ptd = qtd_from_list_entry( pthis );
+ elem_pool_lock( qh_pool, TRUE );
+ pelnk = elem_pool_alloc_elem( qh_pool );
+ elem_pool_unlock( qh_pool, TRUE);
+
+ if( pelnk == NULL )
+ {
+ elem_safe_free( pthis, FALSE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+ pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ pqhc = ( PEHCI_QH_CONTENT )pqh;
+
+ pqh->hw_alt_next = pqh->hw_next = EHCI_PTR_TERM;
+
+ pqhc->dev_addr = dev_addr;
+ pqhc->inactive = 0;
+ pqhc->endp_addr = endp_num( purb->pendp );
+
+ if( pipe_content->speed_high )
+ pqhc->endp_spd = USB_SPEED_HIGH;
+ else if( pipe_content->speed_low )
+ pqhc->endp_spd = USB_SPEED_LOW;
+ else
+ pqhc->endp_spd = USB_SPEED_FULL;
+
+ pqhc->data_toggle = 1; // use dt from qtd
+ pqhc->is_async_head = 0;
+ pqhc->max_packet_size = endp_max_packet_size( purb->pendp );
+
+ if( pipe_content->speed_high == 0 )
+ pqhc->is_ctrl_endp = 1;
+ else
+ pqhc->is_ctrl_endp = 0;
+
+ pqhc->reload_counter = EHCI_NAK_RL_COUNT;
+
+ // DWORD 2
+ pqh->hw_info2 = 0;
+ pqhc->mult = 1;
+
+ if( !pipe_content->speed_high )
+ {
+ pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr;
+ pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx;
+ }
+
+ purb->td_count = td_count;
+
+ ehci_insert_tds_qh( ehci, pqh, ptd );
+ ehci_insert_qh_urb( purb, pqh );
+
+ usb_endp_busy_count_inc( purb->pendp );
+ ehci_insert_urb_to_schedule( ehci, purb, ret );
+
+ if( ret == FALSE )
+ {
+ RemoveEntryList( &purb->trasac_list );
+ RemoveEntryList( &pqh->elem_head_link->elem_link );
+
+ elem_safe_free( &pqh->elem_head_link->elem_link, TRUE );
+ elem_safe_free( pthis, FALSE );
+
+ InitializeListHead( &purb->trasac_list );
+ // usb_endp_busy_count_dec( purb->pendp );
+ return STATUS_UNSUCCESSFUL;
+ }
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+ehci_internal_submit_int(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ LIST_ENTRY td_list, *pthis, *pnext;
+ LONG i, max_packet_size;
+ PEHCI_QTD ptd;
+ BOOL ret;
+ PUSB_DEV pdev;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ UCHAR mult_trans, toggle, old_toggle;
+ PEHCI_ELEM_LINKS pelnk;
+ PEHCI_QTD_CONTENT ptdc;
+ PEHCI_QH pqh;
+ PEHCI_QH_CONTENT pqhc;
+ PEHCI_FSTN pfstn;
+
+ if( ehci == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ old_toggle = toggle = ( purb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE;
+ max_packet_size = endp_max_packet_size( purb->pendp );
+ pdev = dev_from_endp( purb->pendp );
+
+ if( max_packet_size == 0 || max_packet_size > 64 )
+ return STATUS_INVALID_PARAMETER;
+
+ if( ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) == 0 )
+ {
+ if( max_packet_size < purb->data_length )
+ return STATUS_INVALID_PARAMETER;
+
+ for( i = 1; i < 16; i ++ )
+ {
+ if ( ( ( ( ULONG )purb->pendp->pusb_endp_desc->bInterval ) >> i ) == 0 )
+ break;
+ }
+ i--;
+ mult_trans = 1;
+ }
+ else
+ {
+ mult_trans = endp_mult_count( purb->pendp );
+ if( max_packet_size * endp_mult_count( purb->pendp ) < purb->data_length )
+ return STATUS_INVALID_PARAMETER;
+ i = purb->pendp->pusb_endp_desc->bInterval - 1;
+ }
+
+ purb->pipe = 0;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ pipe_content->interval = i;
+ pipe_content->trans_type = USB_ENDPOINT_XFER_INT;// bit 0-1
+ pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; // bit 5
+ pipe_content->speed_low = ( pdev->flags & USB_DEV_FLAG_LOW_SPEED ) ? 1 : 0; // bit 6
+ pipe_content->trans_dir = endp_dir( purb->pendp ) == USB_DIR_IN ? 1 : 0; // bit 7
+ pipe_content->dev_addr = pdev->dev_addr; // bit 8-14
+ pipe_content->endp_addr = endp_num( purb->pendp ); // bit 15-18
+ pipe_content->data_toggle = 1; // bit 19
+ pipe_content->mult_count = mult_trans;
+
+ // pipe_content->start_uframe : 3; // bit 28-30 will be filled later
+
+ for( i = 1; i <= 16; i++ )
+ {
+ if( ( ( ULONG )max_packet_size ) >> i )
+ continue;
+ else
+ break;
+ }
+ i --;
+ i &= 0xf;
+
+ pipe_content->max_packet_size = i; // bit 20-23 log2( max_packet_size )
+
+ if( ehci_claim_bandwidth( ehci, purb, TRUE ) == FALSE )
+ {
+ // can not allocate bandwidth for it
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ // one qtd is enough
+ elem_pool_lock( qtd_pool, TRUE );
+ pelnk = elem_pool_alloc_elem( qtd_pool );
+ elem_pool_unlock( qtd_pool, TRUE );
+
+ if( pelnk == NULL )
+ {
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ ptd = ( PEHCI_QTD )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ ptdc = ( PEHCI_QTD_CONTENT )ptd;
+ ptd->hw_next = EHCI_PTR_TERM;
+ // DWORD 1
+ ptd->hw_alt_next = EHCI_PTR_TERM;
+ // DWORD 2
+ ptdc->status = 0x80;
+ ptdc->pid = pipe_content->trans_dir ? QTD_PID_IN : QTD_PID_OUT;
+ ptdc->err_count = 3;
+ ptdc->cur_page = 0;
+ ptdc->ioc = 1;
+ ptdc->bytes_to_transfer = purb->data_length;
+ toggle = ( UCHAR )ehci_fill_td_buf_ptr( purb, 0, &pelnk->elem_link, 1, toggle );
+
+ elem_pool_lock( qh_pool, TRUE );
+ pelnk = elem_pool_alloc_elem( qh_pool );
+ elem_pool_unlock( qh_pool, TRUE );
+ if( pelnk == NULL )
+ {
+ elem_safe_free( &ptd->elem_head_link->elem_link, TRUE );
+ InitializeListHead( &purb->trasac_list );
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+ pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ pqhc = ( PEHCI_QH_CONTENT )pqh;
+
+ pqh->hw_next = EHCI_PTR_TERM;
+ pqhc->dev_addr = pdev->dev_addr;
+ pqhc->inactive = 0;
+ pqhc->endp_addr = endp_num( purb->pendp );
+
+ if( pipe_content->speed_high )
+ pqhc->endp_spd = USB_SPEED_HIGH;
+ else if( pipe_content->speed_low )
+ pqhc->endp_spd = USB_SPEED_LOW;
+ else
+ pqhc->endp_spd = USB_SPEED_FULL;
+
+ pqhc->data_toggle = 0;
+ pqhc->is_async_head = 0;
+ pqhc->max_packet_size = endp_max_packet_size( purb->pendp );
+ pqhc->is_ctrl_endp = 0;
+ pqhc->reload_counter = 0;
+
+ // DWORD 2
+ pqh->hw_info2 = 0;
+ pqhc->mult = mult_trans;
+
+ if( pipe_content->speed_high )
+ {
+ if( pipe_content->interval == 0 ) // one poll per uframe
+ pqhc->s_mask = 0xff;
+ else if( pipe_content->interval == 1 ) // one poll every 2 uframe
+ pqhc->s_mask = pipe_content->start_uframe == 0 ? 0x55 : 0xbb;
+ else if( pipe_content->interval == 2 )
+ {
+ pqhc->s_mask = 0x11;
+ pqhc->s_mask <<= pipe_content->start_uframe;
+ }
+ else
+ {
+ pqhc->s_mask = 1 << ( pipe_content->start_uframe );
+ }
+ pqhc->c_mask = 0;
+ }
+ else // full/low speed
+ {
+ pqhc->s_mask = 1 << pipe_content->start_uframe;
+ if( pipe_content->start_uframe < 4 )
+ {
+ pqhc->c_mask = 0x07 << ( pipe_content->start_uframe + 2 );
+ }
+ else if( pipe_content->start_uframe == 4 )
+ {
+ pqhc->c_mask = 0xc1;
+ }
+ else if( pipe_content->start_uframe >= 5 )
+ {
+ // we need fstn
+ pqhc->c_mask = 0x03;
+ if( pipe_content->start_uframe == 5 )
+ {
+ pqhc->c_mask |= 0x80;
+ }
+ }
+ if( pipe_content->start_uframe >= 4 )
+ {
+ // chain an fstn
+ elem_pool_lock( fstn_pool, TRUE );
+ pelnk = elem_pool_alloc_elem( fstn_pool );
+ elem_pool_unlock( fstn_pool, TRUE );
+ if( pelnk == NULL )
+ {
+ elem_safe_free( &pqh->elem_head_link->elem_link, TRUE );
+ elem_safe_free( &ptd->elem_head_link->elem_link, TRUE );
+ InitializeListHead( &purb->trasac_list );
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+ pfstn = ( PEHCI_FSTN )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ pfstn->hw_prev = ptd->phys_addr;
+ pfstn->elem_head_link->purb = purb;
+ InsertTailList( &ptd->elem_head_link->elem_link, &pfstn->elem_head_link->elem_link );
+ }
+ pqhc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr;
+ pqhc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx;
+ }
+
+ // DWORD 3
+ purb->td_count = 1;
+
+ InitializeListHead( &purb->trasac_list );
+ ehci_insert_tds_qh( ehci, pqh, ptd );
+ ehci_insert_qh_urb( purb, pqh );
+
+ purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle << 31 );
+ usb_endp_busy_count_inc( purb->pendp );
+
+ ehci_insert_urb_to_schedule( ehci, purb, ret );
+
+ if( ret == FALSE )
+ {
+ RemoveEntryList( &purb->trasac_list );
+ RemoveEntryList( &pqh->elem_head_link->elem_link );
+
+ elem_safe_free( &pqh->elem_head_link->elem_link, TRUE );
+ // an fstn may follow the td
+ elem_safe_free( &ptd->elem_head_link->elem_link, FALSE );
+
+ InitializeListHead( &purb->trasac_list );
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+
+ purb->pendp->flags = ( purb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( ( toggle ^ 1 ) << 31 );
+ // usb_endp_busy_count_dec( purb->pendp );
+
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+ehci_internal_submit_iso(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ LONG i, j, td_count, temp;
+ PEHCI_ITD pitd;
+ PEHCI_SITD psitd;
+ PEHCI_SITD_CONTENT psitdc;
+ PEHCI_ITD_CONTENT pitdc;
+ LIST_ENTRY td_list, *pthis, *pnext, *pprev;
+ BOOL toggle, ret;
+ KIRQL old_irql;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PUSB_DEV pdev;
+ PEHCI_ELEM_LINKS pelnk;
+
+ if( ehci == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( purb->iso_frame_count == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ pdev = dev_from_endp( purb->pendp );
+ purb->pipe = 0;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ pipe_content->trans_type = USB_ENDPOINT_XFER_ISOC; // bit 0-1
+ pipe_content->speed_high = ( pdev->flags & USB_DEV_FLAG_HIGH_SPEED ) ? 1 : 0; // bit 5
+ pipe_content->speed_low = 0; // bit 6
+ pipe_content->trans_dir = endp_dir( purb->pendp ) == USB_DIR_IN ? 1 : 0; // bit 7
+ pipe_content->dev_addr = pdev->dev_addr; // bit 8-14
+ pipe_content->endp_addr = endp_num( purb->pendp ); // bit 15-18
+ pipe_content->data_toggle = 0; // bit 19
+
+ ret = FALSE;
+ purb->params[ 0 ] = j = endp_max_packet_size( purb->pendp );
+
+ if( pipe_content->speed_high == 0 )
+ {
+ // check to see if the frame data is too long to transfer
+ if( purb->iso_frame_count >= ( LONG )ehci->frame_count )
+ return STATUS_INVALID_PARAMETER;
+
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ if( purb->iso_packet_desc[ i ].length > j )
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ // excess the frame count limit
+ if( purb->iso_frame_count >= ( LONG )( ehci->frame_count << 3 ) )
+ return STATUS_INVALID_PARAMETER;
+
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ if( purb->iso_packet_desc[ i ].length > j * endp_mult_count( purb->pendp ) ) // 3 is max mult-transaction count
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pipe_content->mult_count = endp_mult_count( purb->pendp );
+ }
+
+ pipe_content->max_packet_size = 0; // bit 20-23 log( max_packet_size ), not correct, should not be used
+
+ if( pipe_content->speed_high == 0 )
+ {
+ for( i = 1; i < 16; i ++ )
+ {
+ if ( ( ( ( ULONG )purb->pendp->pusb_endp_desc->bInterval ) >> i ) == 0 )
+ break;
+ }
+ i--;
+ }
+ else
+ {
+ i = purb->pendp->pusb_endp_desc->bInterval - 1;
+ }
+
+ pipe_content->interval = i; // bit 24-27 the same definition as in USB2.0 spec, for high or full/low speed
+
+ if( ehci_claim_bandwidth( ehci, purb, TRUE ) == FALSE )
+ return STATUS_UNSUCCESSFUL;
+
+ if( pipe_content->speed_high == 0 )
+ {
+ td_count = purb->iso_frame_count;
+
+ // test to see if the last td needs one more sitd for pure complete-split
+ if( pipe_content->trans_dir == 0 )
+ {
+ j = ( purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length + 187 ) / 188;
+ if( purb->iso_packet_desc[ purb->iso_frame_count - 1 ].params.start_uframe + 1 + j >= 8 )
+ {
+ td_count++;
+ ret = TRUE;
+ }
+ }
+ elem_pool_lock( itd_pool, TRUE );
+ pelnk = elem_pool_alloc_elems( itd_pool, td_count );
+ elem_pool_unlock( itd_pool, TRUE );
+
+ }
+ else
+ {
+ i = REAL_INTERVAL;
+ if( pipe_content->interval >= 3 )
+ {
+ td_count = purb->iso_frame_count;
+ j = 0;
+ }
+ else
+ {
+ j = purb->iso_start_frame & 0x07;
+ if( j == 0 )
+ {
+ td_count = ( purb->iso_frame_count + 8 / i - 1 ) * i / 8;
+ }
+ else
+ {
+ j = 1 + ( 7 - j ) / i; // the leading packets from the 8-trans boundary
+ td_count = ( j >= ( LONG )purb->iso_frame_count ?
+ 1 : 1 + ( purb->iso_frame_count - j + 8 / i - 1 ) * i / 8 );
+ }
+ }
+
+ elem_pool_lock( sitd_pool, TRUE );
+ pelnk = elem_pool_alloc_elems( sitd_pool, td_count );
+ elem_pool_unlock( sitd_pool, TRUE );
+ }
+
+ if( pelnk == NULL )
+ {
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ InsertTailList( &pelnk->elem_link, &td_list );
+ ListFirst( &td_list, pthis );
+ pprev = pthis;
+ purb->td_count = td_count;
+
+ //set up offset for high speed and interval == 1
+ if( pipe_content->speed_high && pipe_content->interval == 0 )
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ if( i == 0 )
+ purb->iso_packet_desc[ i ].offset = 0;
+ else
+ purb->iso_packet_desc[ i ].offset = purb->iso_packet_desc[ i - 1 ].offset +
+ purb->iso_packet_desc[ i ].length;
+ }
+ }
+
+ i = 0, temp = 0;
+
+ while( pthis )
+ {
+ init_elem_phys_part( struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ) );
+ if( pipe_content->speed_high )
+ {
+ LONG start_uframe, k;
+ LONG l, pk_idx, offset, start_uf, td_length;
+ PULONG pbuf;
+ ULONG phys_addr[ 8 ];
+
+ pitd = itd_from_list_entry( pthis );
+ pitdc = ( PEHCI_ITD_CONTENT )pitd;
+ start_uframe = purb->iso_start_frame & 0x07;
+
+ // will be filled later
+ pitd->hw_next = EHCI_PTR_TERM;
+
+ // DWORD 9;
+ pitdc->dev_addr = pdev->dev_addr;
+ pitdc->endp_num = endp_num( purb->pendp );
+
+ pitdc->max_packet_size = endp_max_packet_size( purb->pendp );
+ pitdc->io_dir = pipe_content->trans_dir;
+ pitdc->mult = endp_mult_count( purb->pendp );
+
+ pbuf = pitd->hw_bufp;
+ RtlZeroMemory( phys_addr, sizeof( phys_addr ) );
+
+ if( pipe_content->interval < 3 )
+ {
+ // this indicates one itd schedules more than one uframes
+ // for multiple transactions described by iso_packet_desc
+ if( i == 0 )
+ k = td_count == 1 ? purb->iso_frame_count : j; // the first itd
+ else
+ k = ( LONG )( purb->iso_frame_count - i ) <= 8 / REAL_INTERVAL
+ ? ( purb->iso_frame_count - i ) : 8 / REAL_INTERVAL;
+
+ // j is the header transactions out of the interval
+ // aligned transactions per td
+ if( j > 0 && i == 0 ) // handle the first itd
+ start_uf = start_uframe;
+ else
+ start_uf = start_uframe % REAL_INTERVAL;
+ }
+ else
+ {
+ k = 1, start_uf = start_uframe & 0x07;
+ }
+
+
+ // calculate the data to transfer with this td
+ td_length = 0;
+ for( l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l += REAL_INTERVAL )
+ {
+ td_length += purb->iso_packet_desc[ pk_idx ].length;
+ phys_addr[ l ] = MmGetPhysicalAddress( &purb->data_buffer[ purb->iso_packet_desc[ pk_idx ].offset ] ).LowPart;
+ }
+
+ // fill the page pointer, and offset
+ if( pipe_content->interval != 0 )
+ {
+ for( l = start_uf, pk_idx = i; pk_idx < i + k; pk_idx++, l+= REAL_INTERVAL )
+ {
+ pitdc->status_slot[ l ].offset = phys_addr[ l ] & ( PAGE_SIZE - 1 );
+ pbuf[ l >> pipe_content->interval ] |= phys_addr[ l ] & ( ~( PAGE_SIZE - 1 ) );
+ pitdc->status_slot[ l ].page_sel = l >> pipe_content->interval;
+ pitdc->status_slot[ l ].status = 0x08;
+ pitdc->status_slot[ l ].trans_length = purb->iso_packet_desc[ pk_idx ].length;
+ if( PAGE_SIZE - pitdc->status_slot[ l ].offset < ( ULONG )purb->iso_packet_desc[ pk_idx ].length )
+ {
+ // fill the next page buf, we can not simply add
+ // PAGE_SIZE to the phys_addr[ l ].
+ pbuf[ ( l >> pipe_content->interval ) + 1 ] |=
+ MmGetPhysicalAddress(
+ ( PBYTE )( ( ( ULONG )&purb->data_buffer[ purb->iso_packet_desc[ pk_idx ].offset ] ) & ( ~( PAGE_SIZE - 1 ) ) )
+ + PAGE_SIZE ).LowPart;
+ }
+ }
+ }
+ else // interval == 0
+ {
+ LONG m, n, n2;
+ // fill the page buffer first
+ // calculate the page buffer needed
+ offset = phys_addr[ 0 ] & ( PAGE_SIZE - 1 );
+ if( offset != 0 )
+ {
+ offset = PAGE_SIZE - offset;
+ l = 1 + ( td_length - offset + PAGE_SIZE - 1 ) / PAGE_SIZE;
+ }
+ else
+ {
+ l = ( td_length + PAGE_SIZE - 1 ) / PAGE_SIZE;
+ }
+
+ if( l > 7 )
+ TRAP();
+
+ // fill the hw_bufp array and PG field, pk_idx is index into hw_bufp
+ for( pk_idx = 0; pk_idx < l; pk_idx++ )
+ {
+ if( pk_idx == 0 )
+ {
+ offset = phys_addr[ start_uf ] & ( ~( PAGE_SIZE - 1 ) );
+ pbuf[ pk_idx ] |= offset;
+ n = pk_idx;
+ pitdc->status_slot[ 0 ].page_sel = n;
+ n2 = start_uf;
+ }
+ else
+ {
+ // scan to find if the buf pointer already filled in the td
+ // since interval = 1, we do not need k * REAL_INTERVAL
+ // k is transaction count for current td,
+ // n is hw_bufp( pbuf ) index
+ // n2 is the last phys_addr index we stopped
+ for( m = n2; m < start_uf + k ; m++ )
+ {
+ // we can not determine the phys_addr[ x ] is piror
+ // to offset if it is less than offset.
+ // because phys_addr is discrete.
+ // if( ( phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ) ) < offset )
+ // continue;
+
+ if( ( phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) ) ) == ( ULONG )offset )
+ {
+ pitdc->status_slot[ m ].page_sel = n;
+ continue;
+ }
+ break;
+ }
+
+ if( m == start_uf + k )
+ TRAP();
+
+ offset = phys_addr[ m ] & ( ~( PAGE_SIZE - 1 ) );
+ pbuf[ pk_idx ] |= offset;
+ n = pk_idx;
+ n2 = m;
+ pitdc->status_slot[ m ].page_sel = n;
+ }
+ }
+ // fill offset and others
+ for( l = start_uf, pk_idx = i; l < start_uf + k; l++, pk_idx++ )
+ {
+ pitdc->status_slot[ l ].offset = ( phys_addr[ l ] & ( PAGE_SIZE - 1 ) );
+ pitdc->status_slot[ l ].status = 0x08;
+ pitdc->status_slot[ l ].trans_length = purb->iso_packet_desc[ pk_idx ].length;
+ }
+ // exhausted
+ }
+ i += k;
+ }
+ else // full/low speed
+ {
+ psitd = sitd_from_list_entry( pthis );
+ psitdc = ( PEHCI_SITD_CONTENT )psitd;
+ psitd->hw_next = EHCI_PTR_TERM;
+
+ // DWORD 1;
+ psitdc->dev_addr = pdev->dev_addr;
+ psitdc->endp_num = endp_num( purb->pendp );
+ psitdc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr;
+ psitdc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx;
+ psitdc->io_dir = endp_dir( purb->pendp );
+
+ psitdc->status &= 0x80; // in DWORD 3
+
+ // DWORD 2;
+ j = ( purb->iso_packet_desc[ i ].length + 187 ) / 188;
+
+ if( psitdc->io_dir == 0 )
+ {
+ for( ; j > 0; j-- )
+ {
+ psitdc->s_mask |= ( 1 << ( j - 1 ) );
+ }
+ psitdc->s_mask <<= purb->iso_packet_desc[ i ].params.start_uframe & 0x07;
+ psitdc->c_mask = 0;
+ }
+ else
+ {
+ LONG k;
+
+ psitdc->s_mask = 1 << purb->iso_packet_desc[ i ].params.start_uframe & 0x07;
+ // iso split case 2b: ehci spec 1.0
+ if( j == 6 )
+ j = 5;
+
+ j = j - 1 + 2; // actual complete-split count
+
+ psitdc->c_mask |= temp >> 8; // the previous sitd's complete split
+ if( temp >> 8 ) // link back for sitd split completion
+ {
+ psitd->hw_backpointer = sitd_from_list_entry( pprev )->phys_addr;
+ psitdc->status &= 0x82;
+ }
+ else
+ {
+ psitd->hw_backpointer = EHCI_PTR_TERM;
+ }
+
+ for( k = temp = 0; k < j; k++ )
+ {
+ temp |= 1 << k;
+ }
+
+ temp <<= ( ( purb->iso_packet_desc[ i ].params.start_uframe & 0x07 ) + 2 );
+
+ // only uframe zero and one have complete split for prev sitd
+ if( ( temp >> 8 ) > 3 )
+ TRAP();
+
+ psitdc->c_mask |= temp & 0xff;
+ }
+
+ // DWORD 3:
+ psitdc->c_prog_mask = 0;
+ psitdc->bytes_to_transfer = purb->iso_packet_desc[ i ].length;
+ psitdc->page_sel = 0;
+ psitdc->ioc = 0;
+
+ // DWORD 4;
+ j = ( ULONG )( ( PBYTE )purb->data_buffer + purb->iso_packet_desc[ i ].offset );
+ psitd->hw_tx_results2 = MmGetPhysicalAddress( ( PVOID )j ).LowPart;
+
+ // DWORD 5;
+ if( PAGE_SIZE - ( j & ( PAGE_SIZE - 1 ) ) < ( ULONG )purb->iso_packet_desc[ i ].length )
+ {
+ // need to fill another slot
+ psitdc->page1 = MmGetPhysicalAddress ( ( PVOID ) ( ( j & ~( PAGE_SIZE - 1 ) ) + PAGE_SIZE ) ).LowPart >> 12;
+ }
+
+ if( purb->iso_packet_desc[ i ].length > 188 )
+ psitdc->trans_pos = 0x00;
+ else if( purb->iso_packet_desc[ i ].length <= 188 )
+ psitdc->trans_pos = 0x01;
+
+ if( psitdc->io_dir == 0 )
+ psitdc->trans_count = ( purb->iso_packet_desc[ i ].length + 187 ) / 188;
+
+ }
+ ListNext( &td_list, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+
+ }
+
+ if( pipe_content->speed_high == 0 )
+ {
+ // has an extra sitd to fill at the tail
+ if( ret )
+ {
+ ListFirstPrev( &td_list, pthis );
+ init_elem_phys_part( struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link ) );
+
+ psitd = sitd_from_list_entry( pthis );
+ psitdc = ( PEHCI_SITD_CONTENT )psitd;
+ psitd->hw_next = EHCI_PTR_TERM;
+
+ // DWORD 1;
+ psitdc->dev_addr = pdev->dev_addr;
+ psitdc->endp_num = endp_num( purb->pendp );
+ psitdc->hub_addr = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->hub_addr;
+ psitdc->port_idx = ( ( PURB_HS_CONTEXT_CONTENT )&purb->hs_context )->port_idx;
+ psitdc->io_dir = endp_dir( purb->pendp );
+
+ psitdc->status &= 0x80; // in DWORD 3
+
+ // DWORD 2;
+ psitdc->s_mask = 0x04; // uframe 2, random selection
+
+ psitdc->c_mask = 0x70; // complete split at uframe 4, 5, 6
+ ListFirstPrev( pthis, pprev );
+ psitd->hw_backpointer = sitd_from_list_entry( pprev )->phys_addr;
+ psitdc->status &= 0x82;
+
+ // DWORD 3:
+ psitdc->c_prog_mask = 0;
+ psitdc->bytes_to_transfer = 1; // purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length;
+ psitdc->page_sel = 0;
+
+ j = ( ULONG )( ( PBYTE )purb->data_buffer + purb->iso_packet_desc[ purb->iso_frame_count - 1 ].offset );
+ // the last byte is overridden.
+ j += purb->iso_packet_desc[ purb->iso_frame_count - 1 ].length - 1;
+ psitd->hw_tx_results2 = MmGetPhysicalAddress( ( PVOID )j ).LowPart;
+ }
+
+ // set the interrupt
+ ListFirstPrev( &td_list, pthis );
+ psitdc = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis );
+ psitdc->ioc = 1;
+ }
+ else
+ {
+ // set the ioc
+ ListFirstPrev( &td_list, pthis );
+ pitdc = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis );
+ for( i = 7; i >= 0; i-- )
+ {
+ if( pitdc->status_slot[ i ].status = 0x08 )
+ {
+ pitdc->status_slot[ i ].ioc = 1;
+ break;
+ }
+ }
+ if( i < 0 )
+ TRAP();
+ }
+
+ ListFirst( &td_list, pthis );
+ // ListFirst( &purb->trasac_list, pthis )
+ RemoveEntryList( &td_list );
+ InsertTailList( pthis, &purb->trasac_list );
+
+ while( pthis )
+ {
+ // fill the purb ptr
+ struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->purb = purb;
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ //indirectly guarded by pending_endp_list_lock
+ usb_endp_busy_count_inc( purb->pendp );
+ ehci_insert_urb_to_schedule( ehci, purb, ret );
+
+ if( ret == FALSE )
+ {
+ // usb_endp_busy_count_dec( purb->pendp );
+
+ ListFirst( &purb->trasac_list, pthis );
+ RemoveEntryList( &purb->trasac_list );
+
+ elem_safe_free( pthis, FALSE );
+ ehci_claim_bandwidth( ehci, purb, FALSE );
+ return STATUS_UNSUCCESSFUL;
+ }
+ return STATUS_SUCCESS;
+}
+
+BOOLEAN
+ehci_sync_insert_urb_schedule(
+PVOID context
+)
+//this function used as the KeSynchronizeExecution param to delegate control to ehci_insert_urb_schedule
+{
+ PSYNC_PARAM sync_param;
+ PEHCI_DEV ehci;
+ PURB purb;
+
+ sync_param = ( PSYNC_PARAM ) context;
+ if( sync_param == NULL )
+ return FALSE;
+
+ ehci = sync_param->ehci;
+ purb = ( PURB )sync_param->context;
+
+ if( ehci == NULL || purb == NULL )
+ return ( UCHAR )sync_param->ret = FALSE;
+
+ return ( UCHAR )( sync_param->ret = ehci_insert_urb_schedule( ehci, purb ) );
+}
+
+BOOLEAN
+ehci_sync_cancel_urb(
+PVOID context
+)
+{
+ //cancel a single purb
+ PEHCI_DEV ehci;
+ PSYNC_PARAM sync_param;
+ PURB purb2, dest_urb;
+ PLIST_ENTRY pthis, pnext;
+ BOOL found = FALSE;
+
+ if( context == NULL )
+ return FALSE;
+
+ sync_param = ( PSYNC_PARAM )context;
+ ehci = sync_param->ehci;
+ dest_urb = ( PURB )sync_param->context;
+
+ if( ehci == NULL || dest_urb == NULL )
+ return ( UCHAR )sync_param->ret = FALSE;
+
+ ListFirst( &ehci->urb_list, pthis );
+ while( pthis )
+ {
+ purb2 = ( PURB ) pthis;
+ if( purb2 == dest_urb )
+ {
+ found = TRUE;
+ purb2->flags |= URB_FLAG_FORCE_CANCEL;
+ break;
+ }
+ ListNext( &ehci->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ if( found )
+ {
+ press_doorbell( ehci );
+ }
+ return ( UCHAR )( sync_param->ret = found );
+}
+
+NTSTATUS
+ehci_cancel_urb(
+PEHCI_DEV ehci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+//note any fields of the purb can not be referenced unless it is found in some queue
+{
+
+ NTSTATUS status;
+ PLIST_ENTRY pthis, pnext;
+ BOOL found;
+ PURB purb2;
+
+ SYNC_PARAM sync_param;
+
+ USE_IRQL;
+
+ if( ehci == NULL || purb == NULL || pdev == NULL || pendp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( pdev, FALSE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ //delegate to remove device for this job
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ if( dev_from_endp( pendp ) != pdev )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if( endp_state( pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ //it will be canceled in ehci_process_pending_endp
+ unlock_dev( pdev, FALSE );
+ return USB_STATUS_ENDPOINT_HALTED;
+ }
+
+ found = FALSE;
+ ListFirst( &pendp->urb_list, pthis );
+ while( pthis )
+ {
+ purb2 = ( PURB ) pthis;
+ if( purb2 == purb )
+ {
+ found = TRUE;
+ RemoveEntryList( pthis );
+ InitializeListHead( pthis );
+ break;
+ }
+ ListNext( &pendp->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+ unlock_dev( pdev, FALSE );
+
+ if( found )
+ {
+ purb->status = STATUS_CANCELLED;
+
+ ehci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, FALSE );
+ pdev->ref_count --;
+ unlock_dev( pdev, FALSE );
+ return STATUS_SUCCESS;
+ }
+
+ // search the purb in the purb-list and try to cancel
+ sync_param.ehci = ehci;
+ sync_param.context = purb;
+
+ KeSynchronizeExecution( ehci->pdev_ext->ehci_int, ehci_sync_cancel_urb, &sync_param );
+
+ found = sync_param.ret;
+
+ if( found )
+ return USB_STATUS_CANCELING;
+
+ return STATUS_INVALID_PARAMETER;
+}
+
+VOID
+ehci_generic_urb_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ KIRQL cur_irql;
+ BOOL is_ctrl;
+ USE_IRQL;
+
+ old_irql = KeGetCurrentIrql();
+ if( old_irql > DISPATCH_LEVEL )
+ TRAP();
+
+ if( old_irql < DISPATCH_LEVEL )
+ KeRaiseIrql(DISPATCH_LEVEL, &old_irql);
+
+ pdev = purb->pdev;
+ if( purb == NULL )
+ goto LBL_LOWER_IRQL;
+
+ if( pdev == NULL )
+ goto LBL_LOWER_IRQL;
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ // no need to do following statistics
+ unlock_dev( pdev, TRUE );
+ goto LBL_CLIENT_PROCESS;
+ }
+ if( usb_error( purb->status ) )
+ {
+ pdev->error_count++;
+ }
+
+ if( purb->pendp == &pdev->default_endp )
+ {
+ if( usb_halted( purb->status ) )
+ {
+ pdev->time_out_count++;
+ if( pdev->time_out_count > 3 )
+ {
+ dev_set_state( pdev, USB_DEV_STATE_ZOMB );
+ ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n", pdev ) );
+ }
+ }
+ else
+ pdev->time_out_count = 0;
+
+ }
+
+ is_ctrl = FALSE;
+ if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_CONTROL )
+ is_ctrl = TRUE;
+
+ unlock_dev( pdev, TRUE );
+
+LBL_CLIENT_PROCESS:
+ if( !is_ctrl )
+ {
+ if( purb->completion )
+ purb->completion( purb, context );
+ }
+ else
+ {
+ PCTRL_REQ_STACK pstack;
+ if( purb->ctrl_req_context.ctrl_stack_count == 0 )
+ {
+ if( purb->completion )
+ purb->completion( purb, context );
+ }
+ else
+ {
+ // pstack = &purb->ctrl_req_stack[ purb->ctrl_req_context.ctrl_cur_stack ];
+ // if( pstack->urb_completion )
+ // pstack->urb_completion( purb, pstack->context );
+ usb_call_ctrl_completion( purb );
+ }
+ }
+
+LBL_LOWER_IRQL:
+ if( old_irql < DISPATCH_LEVEL )
+ KeLowerIrql( old_irql );
+
+ return;
+}
+
+NTSTATUS
+ehci_rh_submit_urb(
+PUSB_DEV pdev,
+PURB purb
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PTIMER_SVC ptimer;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PEHCI_DEV ehci;
+ NTSTATUS status;
+ PHUB2_EXTENSION hub_ext;
+ PUSB_PORT_STATUS ps, psret;
+ LONG i;
+ UCHAR port_count;
+
+ USE_IRQL;
+ if( pdev == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql );
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ ehci = ehci_from_hcd( pdev->hcd );
+ psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet;
+
+ hub_ext = ( ( PHUB2_EXTENSION )pdev->dev_ext );
+ port_count = ( UCHAR )( ( PEHCI_HCS_CONTENT )&ehci->ehci_caps.hcs_params )->port_count;
+
+ switch( endp_type( purb->pendp ) )
+ {
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ if( psetup->bmRequestType == 0xa3 \
+ && psetup->bRequest == USB_REQ_GET_STATUS )
+ {
+ //get-port-status
+ if( psetup->wIndex == 0 \
+ || psetup->wIndex > port_count\
+ || psetup->wLength < 4 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1;
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) );
+ ps = &hub_ext->rh_port_status[ psetup->wIndex ];
+
+ psret = ( PUSB_PORT_STATUS )purb->data_buffer;
+ ps->wPortStatus = 0;
+
+ if( status & PORT_CCS )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_CONNECTION;
+ }
+ if( status & PORT_PE )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_ENABLE;
+ ps->wPortStatus |= USB_PORT_STAT_HIGH_SPEED; // ehci spec
+ }
+ if( status & PORT_PR )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_RESET;
+ }
+ if( status & PORT_SUSP )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_SUSPEND;
+ }
+ if( PORT_USB11( status ) )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+ }
+
+ //always power on
+ ps->wPortStatus |= USB_PORT_STAT_POWER;
+
+ //now set change field
+ if( ( status & PORT_CSC ) && !( ps->wPortStatus & USB_PORT_STAT_LOW_SPEED ) )
+ {
+ ps->wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ }
+ if( ( status & PORT_PEC ) && !( ps->wPortStatus & USB_PORT_STAT_LOW_SPEED ) )
+ {
+ ps->wPortChange |= USB_PORT_STAT_C_ENABLE;
+ }
+
+ //don't touch other fields, might be filled by
+ //other function
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( \
+ "ehci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n", \
+ ps->wPortStatus,
+ ps->wPortChange,
+ ps ) );
+
+ psret->wPortChange = ps->wPortChange;
+ psret->wPortStatus = ps->wPortStatus;
+
+ purb->status = STATUS_SUCCESS;
+
+ break;
+ }
+ else if( psetup->bmRequestType == 0x23 \
+ && psetup->bRequest == USB_REQ_CLEAR_FEATURE )
+ {
+ //clear-port-feature
+ if( psetup->wIndex == 0 || psetup->wIndex > port_count )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1;
+ ps = &hub_ext->rh_port_status[ psetup->wIndex ];
+
+ purb->status = STATUS_SUCCESS;
+ switch( psetup->wValue )
+ {
+ case USB_PORT_FEAT_C_CONNECTION:
+ {
+ SET_RH2_PORTSTAT( i, USBPORTSC_CSC );
+ status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex ) );
+ ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
+ break;
+ }
+ case USB_PORT_FEAT_C_ENABLE:
+ {
+ SET_RH2_PORTSTAT( i, USBPORTSC_PEC );
+ status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex ) );
+ ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE;
+ break;
+ }
+ case USB_PORT_FEAT_C_RESET:
+ {
+ ps->wPortChange &= ~USB_PORT_STAT_C_RESET;
+ //the reset signal is down in rh_timer_svc_reset_port_completion
+ // enable or not is set by host controller
+ // status = EHCI_READ_PORT_ULONG( ( PUSHORT ) ( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n", psetup->wIndex ) );
+ break;
+ }
+ case USB_PORT_FEAT_ENABLE:
+ {
+ ps->wPortStatus &= ~USB_PORT_STAT_ENABLE;
+ CLR_RH2_PORTSTAT( i, USBPORTSC_PE );
+ status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex ) );
+ break;
+ }
+ default:
+ purb->status = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+ else if( psetup->bmRequestType == 0xd3 \
+ && psetup->bRequest == HUB_REQ_GET_STATE )
+ {
+ // get bus state
+ if( psetup->wIndex == 0 ||\
+ psetup->wIndex > port_count ||\
+ psetup->wLength == 0 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1;
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) );
+ purb->data_buffer[ 0 ] = ( status & USBPORTSC_LS );
+
+ // reverse the order
+ purb->data_buffer[ 0 ] ^= 0x3;
+ purb->status = STATUS_SUCCESS;
+ break;
+ }
+ else if( psetup->bmRequestType == 0x23 \
+ && psetup->bRequest == USB_REQ_SET_FEATURE )
+ {
+ //reset port
+ if( psetup->wValue != USB_PORT_FEAT_RESET )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue ) );
+ break;
+ }
+
+ i = EHCI_PORTSC + 4 * ( psetup->wIndex - 1 ); // USBPORTSC1;
+
+ ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms
+ ptimer->context = ( ULONG )purb;
+ ptimer->pdev = pdev;
+ ptimer->func = rh_timer_svc_reset_port_completion;
+
+ //start the timer
+ pdev->ref_count += 2; //one for timer and one for purb
+
+ status = EHCI_READ_PORT_ULONG( ( PULONG ) ( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status ) );
+ InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link );
+ purb->status = STATUS_PENDING;
+ }
+ else
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ ptimer->threshold = RH_INTERVAL;
+ ptimer->context = ( ULONG )purb;
+ ptimer->pdev = pdev;
+ ptimer->func = rh_timer_svc_int_completion;
+
+ //start the timer
+ InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link );
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count ) );
+ pdev->ref_count += 2; //one for timer and one for purb
+
+ purb->status = STATUS_PENDING;
+ break;
+ }
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_ISOC:
+ default:
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ unlock_dev( pdev, FALSE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return purb->status;
+}
+
+//must have rh dev_lock acquired
+BOOL
+ehci_rh_reset_port(
+PHCD hcd,
+UCHAR port_idx
+)
+{
+ ULONG i;
+ PEHCI_DEV ehci;
+ ULONG status;
+ UCHAR port_count;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ ehci = ehci_from_hcd( hcd );
+ port_count = ( UCHAR )( ( PEHCI_HCS_CONTENT )&ehci->ehci_caps.hcs_params )->port_count;
+
+ if( port_idx < 1 || port_idx > port_count )
+ return FALSE;
+
+ i = ( ULONG )( EHCI_PORTSC + 4 * ( port_idx - 1 ) );
+
+ // assert the reset signal,(implicitly disable the port)
+ SET_RH2_PORTSTAT( i, PORT_PR );
+
+ usb_wait_ms_dpc( 50 );
+ // clear the reset signal, delay port enable till clearing port feature
+ CLR_RH2_PORTSTAT( i, PORT_PR );
+
+ // wait the port stable
+ usb_wait_ms_dpc( 2 );
+
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) );
+ if( !( status & PORT_PE ) )
+ {
+ // release the ownership from ehci to companion hc
+ status |= PORT_OWNER;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( ehci->port_base + i ), status );
+ // the host controller will set PORTSC automatically
+ return FALSE;
+ }
+ usb_wait_us_dpc( 10 );
+ // SET_RH_PORTSTAT( i, PORT_PE );
+
+ //recovery time 10ms
+ usb_wait_ms_dpc( 10 );
+
+ // clear PORT_PEC and PORT_PCC
+ SET_RH2_PORTSTAT( i, 0x0a );
+
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_reset_port(): status after written=0x%x\n", status ) );
+ return TRUE;
+}
+
+NTSTATUS
+ehci_dispatch_irp(
+IN PDEVICE_OBJECT DeviceObject,
+IN PIRP irp
+)
+{
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+ PEHCI_DEV ehci;
+
+ pdev_ext = DeviceObject->DeviceExtension;
+ ehci = pdev_ext->ehci;
+
+ dev_mgr = ehci->hcd_interf.hcd_get_dev_mgr( &ehci->hcd_interf );
+ return dev_mgr_dispatch( dev_mgr, irp );
+}
+
+//the following are for hcd interface methods
+VOID
+ehci_set_dev_mgr(
+PHCD hcd,
+PUSB_DEV_MANAGER dev_mgr
+)
+
+{
+ hcd->dev_mgr = dev_mgr;
+}
+
+PUSB_DEV_MANAGER
+ehci_get_dev_mgr(
+PHCD hcd
+)
+{
+ return hcd->dev_mgr;
+}
+
+ULONG
+ehci_get_type(
+PHCD hcd
+)
+{
+ return HCD_TYPE_EHCI; // ( hcd->flags & HCD_TYPE_MASK );
+}
+
+VOID
+ehci_set_id(
+PHCD hcd,
+UCHAR id
+)
+{
+ hcd->flags &= ~HCD_ID_MASK;
+ hcd->flags |= ( HCD_ID_MASK & id );
+}
+
+UCHAR
+ehci_get_id(
+PHCD hcd
+)
+{
+ return ( UCHAR )( hcd->flags & HCD_ID_MASK );
+}
+
+
+UCHAR
+ehci_alloc_addr(
+PHCD hcd
+)
+{
+ LONG i;
+ if( hcd == NULL )
+ return 0;
+
+ for( i = 1; i < MAX_DEVS; i ++ )
+ {
+ if( hcd->dev_addr_map[ i >> 3 ] & ( 1 << ( i & 7 ) ) )
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( i >= MAX_DEVS )
+ return 0xff;
+
+ hcd->dev_addr_map[ i >> 3 ] |= ( 1 << ( i & 7 ) );
+ hcd->conn_count++;
+ return ( BYTE )i;
+}
+
+VOID
+ehci_free_addr(
+PHCD hcd,
+UCHAR addr
+)
+{
+ if( addr & 0x80 )
+ return;
+
+ if( hcd == NULL )
+ return;
+
+ hcd->dev_addr_map[ addr >> 3 ] &= ~( 1 << ( addr & 7 ) );
+ return;
+
+}
+
+NTSTATUS
+ehci_submit_urb2(
+PHCD hcd,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb )
+{
+ return ehci_submit_urb( ehci_from_hcd( hcd ), pdev, pendp, purb );
+}
+
+PUSB_DEV
+ehci_get_root_hub(
+PHCD hcd
+)
+{
+ return ehci_from_hcd( hcd )->root_hub;
+}
+VOID
+ehci_set_root_hub(
+PHCD hcd,
+PUSB_DEV root_hub
+)
+{
+ if( hcd == NULL || root_hub == NULL )
+ return;
+ ehci_from_hcd( hcd )->root_hub = root_hub;
+ return;
+}
+
+BOOL
+ehci_remove_device2(
+PHCD hcd,
+PUSB_DEV pdev
+)
+{
+ if( hcd == NULL || pdev == NULL )
+ return FALSE;
+
+ return ehci_remove_device( ehci_from_hcd( hcd ), pdev );
+}
+
+BOOL
+ehci_hcd_release(
+PHCD hcd
+)
+{
+ PEHCI_DEV ehci;
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ ehci = ehci_from_hcd( hcd );
+ pdev_ext = ehci->pdev_ext;
+ return ehci_release( pdev_ext->pdev_obj );
+}
+
+NTSTATUS
+ehci_cancel_urb2(
+PHCD hcd,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+{
+ PEHCI_DEV ehci;
+ if( hcd == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ ehci = ehci_from_hcd( hcd );
+ return ehci_cancel_urb( ehci, pdev, pendp, purb );
+}
+BOOL
+ehci_rh_get_dev_change(
+PHCD hcd,
+PBYTE buf
+) //must have the rh dev_lock acquired
+{
+ PEHCI_DEV ehci;
+ LONG port_count, i;
+ ULONG status;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ ehci = ehci_from_hcd( hcd );
+ port_count = HCS_N_PORTS( ehci->ehci_caps.hcs_params );
+ for( i = 0; i < port_count; i++ )
+ {
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_PORTSC + ( i << 2 ) ) );
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_rh_get_dev_change(): erh port%d status=0x%x\n", i, status ) );
+
+ if( status & ( PORT_PEC | PORT_CSC | PORT_OCC ) )
+ {
+ buf[ ( i + 1 ) >> 3 ] |= ( 1 << ( ( i + 1 ) & 7 ) );
+ }
+ }
+ return TRUE;
+}
+
+NTSTATUS
+ehci_hcd_dispatch(
+PHCD hcd,
+LONG disp_code,
+PVOID param
+)
+{
+ PEHCI_DEV ehci;
+
+ if( hcd == NULL )
+ return STATUS_INVALID_PARAMETER;
+ ehci = ehci_from_hcd( hcd );
+ switch( disp_code )
+ {
+ case HCD_DISP_READ_PORT_COUNT:
+ {
+ if( param == NULL )
+ return STATUS_INVALID_PARAMETER;
+ *( ( PUCHAR )param ) = ( UCHAR )HCS_N_PORTS( ehci->ehci_caps.hcs_params );
+ return STATUS_SUCCESS;
+ }
+ case HCD_DISP_READ_RH_DEV_CHANGE:
+ {
+ if( ehci_rh_get_dev_change( hcd, param ) == FALSE )
+ return STATUS_INVALID_PARAMETER;
+ return STATUS_SUCCESS;
+ }
+ }
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+//------------------------------------------------------------------------------
+// EHCI routines follows
+//
+VOID
+ehci_init_int8_qh(
+PEHCI_QH_CONTENT qh
+);
+
+BOOLEAN
+ehci_cal_cpu_freq(
+PVOID context
+)
+{
+ usb_cal_cpu_freq();
+ return TRUE;
+}
+
+PDEVICE_OBJECT
+ehci_probe(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ LONG bus, i, j, ret;
+ PCI_SLOT_NUMBER slot_num;
+ PPCI_COMMON_CONFIG pci_config;
+ PDEVICE_OBJECT pdev;
+ BYTE buffer[ sizeof( PCI_COMMON_CONFIG ) ];
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+
+ slot_num.u.AsULONG = 0;
+ pci_config = ( PPCI_COMMON_CONFIG ) buffer;
+ pdev = NULL;
+
+ //scan the bus to find ehci controller
+ for( bus = 0; bus < 2; bus++ ) /*enum only bus0 and bus1*/
+ {
+ for( i = 0; i < PCI_MAX_DEVICES; i++ )
+ {
+ slot_num.u.bits.DeviceNumber = i;
+ for( j = 0; j < PCI_MAX_FUNCTIONS; j++ )
+ {
+ slot_num.u.bits.FunctionNumber = j;
+
+ ret = HalGetBusData(
+ PCIConfiguration,
+ bus,
+ slot_num.u.AsULONG,
+ pci_config,
+ PCI_COMMON_HDR_LENGTH );
+
+ if( ret == 0 ) /*no this bus*/
+ break;
+
+ if( ret == 2 ) /*no device on the slot*/
+ break;
+
+ if( pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03 && pci_config->ProgIf == 0x20 )
+ {
+ //well, we find our usb host controller( EHCI ), create device
+ pdev = ehci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr );
+
+ if( !pdev )
+ continue;
+ }
+ }
+
+ if( ret == 0 )
+ break;
+ }
+ }
+
+ if( pdev )
+ {
+ pdev_ext = pdev->DeviceExtension;
+ if( pdev_ext )
+ {
+ // acquire higher irql to eliminate pre-empty
+ KeSynchronizeExecution( pdev_ext->ehci_int, ehci_cal_cpu_freq, NULL );
+ }
+ }
+ return NULL;
+}
+
+PDEVICE_OBJECT
+ehci_alloc(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+ULONG bus_addr,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+
+ LONG frd_num, prd_num;
+ UCHAR buffer[ PCI_COMMON_HDR_LENGTH ];
+ PDEVICE_OBJECT pdev;
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+ ULONG vector, addr_space;
+ LONG bus, i;
+ KIRQL irql;
+ KAFFINITY affinity;
+ UCHAR hcd_id;
+
+ DEVICE_DESCRIPTION dev_desc;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd;
+ PCI_SLOT_NUMBER slot_num;
+ NTSTATUS status;
+
+
+ pdev = ehci_create_device( drvr_obj, dev_mgr );
+
+ if( pdev == NULL )
+ return NULL;
+
+ pdev_ext = pdev->DeviceExtension;
+
+ pdev_ext->pci_addr = bus_addr;
+ bus = ( bus_addr >> 8 );
+
+ slot_num.u.AsULONG = 0;
+ slot_num.u.bits.DeviceNumber = ( ( bus_addr & 0xff ) >> 3 );
+ slot_num.u.bits.FunctionNumber = ( bus_addr & 0x07 );
+
+ //now create adapter object
+ RtlZeroMemory( &dev_desc, sizeof( dev_desc ) );
+
+ dev_desc.Version = DEVICE_DESCRIPTION_VERSION;
+ dev_desc.Master = TRUE;
+ dev_desc.ScatterGather = TRUE;
+ dev_desc.Dma32BitAddresses = TRUE;
+ dev_desc.BusNumber = bus;
+ dev_desc.InterfaceType = PCIBus;
+ dev_desc.MaximumLength = EHCI_MAX_SIZE_TRANSFER;
+
+ pdev_ext->map_regs = 2; // we do not use it seriously
+
+ pdev_ext->padapter = HalGetAdapter( &dev_desc,
+ &pdev_ext->map_regs);
+
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_alloc(): padapter=0x%x\n", pdev_ext->padapter ) );
+ if( pdev_ext->padapter == NULL )
+ {
+ //fatal error
+ ehci_delete_device( pdev );
+ return NULL;
+ }
+
+ DbgPrint("ehci_alloc(): reg_path=0x%x, \n \
+ ehci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \
+ ehci_alloc(): slot_num=0x%x, &res_list=0x%x \n \
+ ehci_alloc(): adapter=0x%x \n", \
+ ( DWORD )reg_path, \
+ ( DWORD )PCIBus, \
+ ( DWORD )bus, \
+ ( DWORD )bus_addr,\
+ ( DWORD )slot_num.u.AsULONG, \
+ ( DWORD )&pdev_ext->res_list, \
+ pdev_ext->padapter);
+
+ //let's allocate resources for this device
+ DbgPrint( "ehci_alloc(): about to assign slot res\n" );
+ if( ( status = HalAssignSlotResources(
+ reg_path,
+ NULL, //no class name yet
+ drvr_obj,
+ NULL, //no support of another ehci controller
+ PCIBus,
+ bus,
+ slot_num.u.AsULONG,
+ &pdev_ext->res_list ) )
+ != STATUS_SUCCESS )
+ {
+ DbgPrint( "ehci_alloc(): error assign slot res, 0x%x\n", status );
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ ehci_delete_device( pdev );
+ return NULL;
+ }
+
+ //parse the resource list
+ for( frd_num = 0; frd_num < ( LONG )pdev_ext->res_list->Count; frd_num ++ )
+ {
+ for( prd_num = 0; prd_num < ( LONG )pdev_ext->res_list->List[ frd_num ].PartialResourceList.Count; prd_num ++ )
+ {
+ pprd = &pdev_ext->res_list->List[ frd_num ].PartialResourceList.PartialDescriptors[ prd_num ];
+ if( pprd->Type == CmResourceTypePort )
+ {
+ RtlCopyMemory( &pdev_ext->res_port, &pprd->u.Port, sizeof( pprd->u.Port ) );
+
+ }
+ else if( pprd->Type == CmResourceTypeInterrupt )
+ {
+ RtlCopyMemory( &pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof( pprd->u.Interrupt ) );
+ }
+ else if( pprd->Type == CmResourceTypeMemory )
+ {
+ RtlCopyMemory( &pdev_ext->res_memory, &pprd->u.Memory, sizeof( pprd->u.Memory ) );
+ }
+ }
+ }
+
+ //for port, translate them to system address
+ addr_space = 0;
+ if( HalTranslateBusAddress(
+ PCIBus,
+ bus,
+ pdev_ext->res_port.Start,
+ &addr_space, //io space
+ &pdev_ext->ehci->ehci_reg_base
+ )
+ != ( BOOLEAN )TRUE )
+ {
+ DbgPrint( "ehci_alloc(): error, can not translate bus address\n" );
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ ehci_delete_device( pdev );
+ return NULL;
+ }
+
+ DbgPrint( "ehci_alloc(): address space=0x%x\n, reg_base=0x%x\n", \
+ addr_space, pdev_ext->ehci->ehci_reg_base.u.LowPart );
+
+ if( addr_space == 0 )
+ {
+ //port has been mapped to memory space
+ pdev_ext->ehci->port_mapped = TRUE;
+ pdev_ext->ehci->port_base = ( PBYTE )MmMapIoSpace(
+ pdev_ext->ehci->ehci_reg_base,
+ pdev_ext->res_port.Length,
+ FALSE );
+
+ //fatal error can not map the registers
+ if( pdev_ext->ehci->port_base == NULL )
+ {
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ ehci_delete_device( pdev );
+ return NULL;
+ }
+ }
+ else
+ {
+ //io space
+ pdev_ext->ehci->port_mapped = FALSE;
+ pdev_ext->ehci->port_base = ( PBYTE )pdev_ext->ehci->ehci_reg_base.LowPart;
+ }
+
+ //before we connect the interrupt, we have to init ehci
+ pdev_ext->ehci->pdev_ext = pdev_ext;
+
+ //init ehci_caps
+ // i = ( ( PEHCI_HCS_CONTENT )( &pdev_ext->ehci->ehci_caps.hcs_params ) )->length;
+
+ ehci_get_capabilities( pdev_ext->ehci, pdev_ext->ehci->port_base );
+ i = pdev_ext->ehci->ehci_caps.length;
+ pdev_ext->ehci->port_base += i;
+
+ if( ehci_init_schedule( pdev_ext->ehci, pdev_ext->padapter ) == FALSE )
+ {
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ ehci_delete_device( pdev );
+ return NULL;
+ }
+
+ InitializeListHead( &pdev_ext->ehci->urb_list );
+ KeInitializeSpinLock( &pdev_ext->ehci->pending_endp_list_lock );
+ InitializeListHead( &pdev_ext->ehci->pending_endp_list );
+
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_alloc(): pending_endp_list=0x%x\n", \
+ &pdev_ext->ehci->pending_endp_list ) );
+
+ init_pending_endp_pool( &pdev_ext->ehci->pending_endp_pool );
+
+ KeInitializeTimer( &pdev_ext->ehci->reset_timer );
+
+ vector = HalGetInterruptVector(
+ PCIBus,
+ bus,
+ pdev_ext->res_interrupt.level,
+ pdev_ext->res_interrupt.vector,
+ &irql,
+ &affinity);
+
+ //connect the interrupt
+ DbgPrint( "ehci_alloc(): the int=0x%x\n", vector );
+ if( IoConnectInterrupt(
+ &pdev_ext->ehci_int,
+ ehci_isr,
+ pdev_ext->ehci,
+ NULL, //&pdev_ext->ehci->frame_list_lock,
+ vector,
+ irql,
+ irql,
+ LevelSensitive,
+ TRUE, //share the vector
+ affinity,
+ FALSE ) //No float save
+ != STATUS_SUCCESS )
+ {
+ ehci_release( pdev );
+ return NULL;
+ }
+
+ KeInitializeDpc( &pdev_ext->ehci_dpc,
+ ehci_dpc_callback,
+ ( PVOID )pdev_ext->ehci );
+
+ return pdev;
+}
+
+PDEVICE_OBJECT
+ehci_create_device(
+PDRIVER_OBJECT drvr_obj,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ NTSTATUS status;
+ PDEVICE_OBJECT pdev;
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+
+ UNICODE_STRING dev_name;
+ UNICODE_STRING symb_name;
+
+ STRING string, another_string;
+ CHAR str_dev_name[ 64 ], str_symb_name[ 64 ];
+ UCHAR hcd_id;
+
+ if( drvr_obj == NULL )
+ return NULL;
+
+ //note: hcd count wont increment till the hcd is registered in dev_mgr
+ sprintf( str_dev_name, "%s%d", EHCI_DEVICE_NAME, dev_mgr->hcd_count );
+ sprintf( str_symb_name, "%s%d",EHCI_DOS_DEVICE_NAME, dev_mgr->hcd_count );
+
+ RtlInitString( &string, str_dev_name );
+ RtlAnsiStringToUnicodeString( &dev_name, &string, TRUE );
+
+ pdev = NULL;
+ status = IoCreateDevice(
+ drvr_obj,
+ sizeof( EHCI_DEVICE_EXTENSION ) + sizeof( EHCI_DEV ),
+ &dev_name,
+ FILE_EHCI_DEV_TYPE,
+ 0,
+ FALSE,
+ &pdev);
+
+ if( status != STATUS_SUCCESS || pdev == NULL )
+ {
+ RtlFreeUnicodeString( &dev_name );
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_create_device(): error create device 0x%x\n", status ) );
+ return NULL;
+ }
+
+ pdev_ext = pdev->DeviceExtension;
+ RtlZeroMemory(
+ pdev_ext,
+ sizeof( EHCI_DEVICE_EXTENSION )
+ + sizeof( EHCI_DEV) );
+
+ pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD;
+ pdev_ext->dev_ext_hdr.dispatch = ehci_dispatch_irp;
+ pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio
+ pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
+
+ pdev_ext->pdev_obj = pdev;
+ pdev_ext->pdrvr_obj = drvr_obj;
+
+ pdev_ext->ehci = ( PEHCI_DEV ) &( pdev_ext[ 1 ] );
+
+ RtlInitString( &another_string, str_symb_name );
+ RtlAnsiStringToUnicodeString( &symb_name, &another_string, TRUE );
+ //RtlInitUnicodeString( &symb_name, DOS_DEVICE_NAME );
+
+ IoCreateSymbolicLink( &symb_name, &dev_name );
+
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, ehci=0x%x, dev_mgr=0x%x\n", \
+ pdev,\
+ pdev_ext,\
+ pdev_ext->ehci, \
+ dev_mgr ) );
+
+ RtlFreeUnicodeString( &dev_name );
+ RtlFreeUnicodeString( &symb_name );
+
+ //register with dev_mgr though it is not initilized
+ ehci_init_hcd_interface( pdev_ext->ehci );
+ hcd_id = dev_mgr_register_hcd( dev_mgr, &pdev_ext->ehci->hcd_interf );
+
+ pdev_ext->ehci->hcd_interf.hcd_set_id( &pdev_ext->ehci->hcd_interf, hcd_id );
+ pdev_ext->ehci->hcd_interf.hcd_set_dev_mgr( &pdev_ext->ehci->hcd_interf, dev_mgr );
+
+ return pdev;
+
+}
+
+VOID
+ehci_init_hcd_interface(
+PEHCI_DEV ehci
+)
+{
+ ehci->hcd_interf.hcd_set_dev_mgr = ehci_set_dev_mgr;
+ ehci->hcd_interf.hcd_get_dev_mgr = ehci_get_dev_mgr;
+ ehci->hcd_interf.hcd_get_type = ehci_get_type;
+ ehci->hcd_interf.hcd_set_id = ehci_set_id;
+ ehci->hcd_interf.hcd_get_id = ehci_get_id;
+ ehci->hcd_interf.hcd_alloc_addr = ehci_alloc_addr;
+ ehci->hcd_interf.hcd_free_addr = ehci_free_addr;
+ ehci->hcd_interf.hcd_submit_urb = ehci_submit_urb2;
+ ehci->hcd_interf.hcd_generic_urb_completion = ehci_generic_urb_completion;
+ ehci->hcd_interf.hcd_get_root_hub = ehci_get_root_hub;
+ ehci->hcd_interf.hcd_set_root_hub = ehci_set_root_hub;
+ ehci->hcd_interf.hcd_remove_device = ehci_remove_device2;
+ ehci->hcd_interf.hcd_rh_reset_port = ehci_rh_reset_port;
+ ehci->hcd_interf.hcd_release = ehci_hcd_release;
+ ehci->hcd_interf.hcd_cancel_urb = ehci_cancel_urb2;
+ ehci->hcd_interf.hcd_start = ehci_start;
+ ehci->hcd_interf.hcd_dispatch = ehci_hcd_dispatch;
+
+ ehci->hcd_interf.flags = HCD_TYPE_EHCI; //hcd types | hcd id
+}
+
+BOOL
+ehci_init_schedule(
+PEHCI_DEV ehci,
+PADAPTER_OBJECT padapter
+)
+{
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+ BOOL ret;
+ LONG i;
+ PEHCI_QH_CONTENT pqh_content;
+ PEHCI_QH pqh;
+ PEHCI_QTD ptd;
+ PEHCI_ELEM_LINKS pelnk;
+ PEHCI_FSTN pfstn;
+
+ if( ehci == NULL )
+ return FALSE;
+
+ pdev_ext = ehci->pdev_ext;
+ if( pdev_ext == NULL )
+ return FALSE;
+
+ // padapter = pdev_ext->padapter;
+ if( ehci->frame_count == 0 )
+ ehci->frame_count = EHCI_DEFAULT_FRAMES;
+
+ // allocate pools
+ for( i = 0; i < 5; i++ )
+ {
+ ret = elem_pool_init_pool( &ehci->elem_pools[ i ], i, padapter );
+ if( ret == FALSE )
+ break;
+ }
+
+ if( ret == FALSE )
+ {
+ i--;
+ for( ; i >= 0; i-- )
+ elem_pool_destroy_pool( &ehci->elem_pools[ i ] );
+ return FALSE;
+ }
+
+ // allocate periodic frame list
+ ehci->frame_list = \
+ HalAllocateCommonBuffer(
+ padapter,
+ sizeof(ULONG) * ehci->frame_count,
+ &ehci->frame_list_phys_addr,
+ FALSE);
+ if( ehci->frame_list == NULL )
+ goto ERROR_OUT;
+
+ RtlZeroMemory( ehci->frame_list, sizeof( ULONG ) * ehci->frame_count );
+ ehci->frame_list_cpu = usb_alloc_mem( NonPagedPool, sizeof( LIST_HEAD ) * ehci->frame_count );
+
+ if( ehci->frame_list_cpu == NULL )
+ goto ERROR_OUT;
+
+ for( i = 0; i < ( LONG )ehci->frame_count; i++ )
+ {
+ InitializeListHead( &ehci->frame_list_cpu[ i ].td_link );
+ }
+
+ for( i = 0; i < 8; i++ )
+ {
+ InitializeListHead( &ehci->periodic_list_cpu[ i ] );
+ }
+
+ InitializeListHead( &ehci->async_list_cpu );
+
+ // init frame band budget array
+ ehci->frame_bw = usb_alloc_mem( NonPagedPool, sizeof( USHORT ) * ehci->frame_count * 8 );
+ if( ehci->frame_bw == NULL )
+ goto ERROR_OUT;
+
+ for( i = 0; i < ( LONG )ehci->frame_count * 8; i++ )
+ {
+ ehci->frame_bw[ i ] = EHCI_MAX_SYNC_BUS_TIME;
+ }
+ ehci->min_bw = EHCI_MAX_SYNC_BUS_TIME;
+
+ // chain the 1ms interrupt qh to the schedule
+ if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] ) ) == NULL )
+ goto ERROR_OUT;
+
+ pqh_content = ( PEHCI_QH_CONTENT )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ ehci_init_int8_qh( pqh_content );
+
+ // chain qh to the shadow list
+ InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], &pelnk->sched_link );
+
+ // chain it to the periodic schedule, we use it as a docking point
+ // for req of 8- uframes request
+ pqh = ( PEHCI_QH )pqh_content;
+
+ for( i = 0; i < ( LONG )ehci->frame_count; i++ )
+ {
+ ehci->frame_list[ i ] = pqh->phys_addr;
+ }
+
+ // allocate fstn
+ /*if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_FSTN_POOL_IDX ] ) ) == NULL )
+ goto ERROR_OUT;
+
+ pfstn = ( PEHCI_FSTN )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ pfstn->hw_next = EHCI_PTR_TERM;
+ pfstn->hw_prev = EHCI_PTR_TERM | ( INIT_LIST_FLAG_QH << 1 );
+ InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_FSTN_INDEX ], &pelnk->sched_link );
+ pqh->hw_next = pfstn->phys_addr;*/
+
+ // allocate for async list head
+ if( ( pelnk = elem_pool_alloc_elem( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] ) ) == NULL )
+ goto ERROR_OUT;
+
+ // init the async list head
+ pqh = ( PEHCI_QH )( ( ULONG )pelnk->phys_part & PHYS_PART_ADDR_MASK );
+ pqh_content = ( PEHCI_QH_CONTENT )pqh;
+ ehci_init_int8_qh( pqh_content );
+ pqh->hw_next = pqh->phys_addr;
+ pqh_content->s_mask = 0;
+ pqh_content->is_async_head = 1;
+ pqh_content->endp_addr = 0;
+ ehci->skel_async_qh = pqh;
+ InsertTailList( &ehci->async_list_cpu, &pqh->elem_head_link->sched_link );
+ return TRUE;
+
+ERROR_OUT:
+ ehci_destroy_schedule( ehci );
+ return FALSE;
+}
+
+BOOL
+ehci_destroy_schedule(
+PEHCI_DEV ehci
+)
+{
+ LONG i;
+ if( ehci == NULL )
+ return FALSE;
+
+ if( ehci->frame_bw )
+ usb_free_mem( ehci->frame_bw );
+ ehci->frame_bw = NULL;
+
+ if( ehci->frame_list_cpu )
+ usb_free_mem( ehci->frame_list_cpu );
+ ehci->frame_list_cpu = NULL;
+
+ if( ehci->frame_list )
+ HalFreeCommonBuffer( \
+ ehci->pdev_ext->padapter,
+ sizeof(ULONG) * ehci->frame_count,
+ ehci->frame_list_phys_addr,
+ ehci->frame_list,
+ FALSE );
+
+ ehci->frame_list = NULL;
+ ehci->frame_list_phys_addr.LowPart = 0;
+ ehci->frame_list_phys_addr.HighPart = 0;
+
+ for( i = 0; i < 5; i++ )
+ elem_pool_destroy_pool( &ehci->elem_pools[ i ] );
+
+ return TRUE;
+}
+
+VOID
+ehci_init_int8_qh(
+PEHCI_QH_CONTENT qh
+)
+{
+ if( qh == NULL )
+ return;
+ // DWORD 0
+ qh->terminal = EHCI_PTR_TERM;
+ qh->ptr_type = 0;
+ qh->reserved = 0;
+ qh->next_link = 0;
+
+ // DWORD 1
+ qh->dev_addr = 126; // a fake addr
+ qh->inactive = 0;
+ qh->endp_addr = 1; // a fake endp
+ qh->endp_spd = USB_SPEED_HIGH;
+ qh->data_toggle = 0;
+ qh->is_async_head = 0;
+ qh->max_packet_size = 64;
+ qh->is_ctrl_endp = 0;
+ qh->reload_counter = 0;
+
+ // DWORD 2
+ qh->s_mask = 0x80; // we are interrupt qh
+ qh->c_mask = 0;
+ qh->hub_addr = 0;
+ qh->port_idx = 0;
+ qh->mult = 1;
+
+ // DWORD 3
+ qh->cur_qtd_ptr = 0; // a terminal
+
+ // overlay
+ // !active and !halted
+ RtlZeroMemory( &qh->cur_qtd, get_elem_phys_part_size( INIT_LIST_FLAG_QTD ) );
+ qh->cur_qtd.alt_terminal = 1; // don't use this
+ qh->cur_qtd.terminal = 1;
+ qh->cur_qtd.status = QTD_STS_HALT;
+}
+
+VOID
+ehci_get_capabilities(
+PEHCI_DEV ehci,
+PBYTE base
+)
+// fetch capabilities register from ehci
+{
+ NTSTATUS status;
+ PEHCI_CAPS pcap;
+ PEHCI_HCS_CONTENT phcs;
+ LONG i;
+
+ if( ehci == NULL )
+ return;
+
+ pcap = &ehci->ehci_caps;
+ pcap->length = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 0 ) );
+ pcap->reserved = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 1 ) );
+ pcap->hci_version = EHCI_READ_PORT_USHORT( ( PUSHORT )( base + 2 ) );
+ pcap->hcs_params = EHCI_READ_PORT_ULONG( ( PULONG )( base + 4 ) );
+ pcap->hcc_params = EHCI_READ_PORT_ULONG( ( PULONG )( base + 8 ) );
+
+ phcs = ( PEHCI_HCS_CONTENT )&pcap->hcs_params;
+ if( phcs->port_rout_rules )
+ {
+ for( i = 0; i < 8; i++ )
+ pcap->portroute [ i ] = EHCI_READ_PORT_UCHAR( ( PUCHAR )( base + 12 + i ) );
+ }
+ return;
+}
+
+BOOL
+ehci_delete_device(
+PDEVICE_OBJECT pdev
+)
+{
+ STRING string;
+ UNICODE_STRING symb_name;
+ CHAR str_symb_name[ 64 ];
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ pdev_ext = pdev->DeviceExtension;
+
+ sprintf( str_symb_name,
+ "%s%d",
+ EHCI_DOS_DEVICE_NAME,
+ pdev_ext->ehci->hcd_interf.hcd_get_id( &pdev_ext->ehci->hcd_interf ) );
+
+ RtlInitString( &string, str_symb_name );
+ RtlAnsiStringToUnicodeString( &symb_name, &string, TRUE );
+ IoDeleteSymbolicLink( &symb_name );
+ RtlFreeUnicodeString( &symb_name );
+
+ if( pdev_ext->res_list )
+ ExFreePool( pdev_ext->res_list ); // not allocated by usb_alloc_mem
+
+ IoDeleteDevice( pdev );
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_delete_device(): device deleted\n" ) );
+ return TRUE;
+}
+
+VOID
+ehci_stop(
+PEHCI_DEV ehci
+)
+{
+ PBYTE base;
+ PEHCI_USBCMD_CONTENT usbcmd;
+ LONG tmp;
+
+ base = ehci->port_base;
+ // turn off all the interrupt
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBINTR ), 0 );
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) );
+ usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp;
+ usbcmd->run_stop = 0;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+}
+
+BOOL
+ehci_release(
+PDEVICE_OBJECT pdev
+)
+{
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+ PEHCI_DEV ehci;
+ KIRQL disp_level;
+ KIRQL old_level;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ pdev_ext = pdev->DeviceExtension;
+
+ if( pdev_ext == NULL )
+ return FALSE;
+
+ ehci = pdev_ext->ehci;
+ if( ehci == NULL )
+ return FALSE;
+
+ ehci_stop( ehci );
+
+ if( pdev_ext->ehci_int )
+ {
+ IoDisconnectInterrupt( pdev_ext->ehci_int );
+ pdev_ext->ehci_int = NULL;
+ }
+ else
+ TRAP();
+ destroy_pending_endp_pool( &pdev_ext->ehci->pending_endp_pool );
+
+ ehci_destroy_schedule( ehci );
+
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+
+ ehci_delete_device( pdev );
+
+ return FALSE;
+
+}
+
+BOOL
+ehci_start(
+PHCD hcd
+)
+{
+ ULONG tmp;
+ PBYTE base;
+ LONG i;
+ PEHCI_USBCMD_CONTENT usbcmd;
+ LARGE_INTEGER interval;
+ PEHCI_DEV ehci;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ ehci = struct_ptr( hcd, EHCI_DEV, hcd_interf );
+ base = ehci->port_base;
+
+ // stop the controller
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) );
+ usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp;
+ usbcmd->run_stop = 0;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+
+ // wait the controller stop( ehci spec, 16 microframe )
+ usb_wait_ms_dpc( 2 );
+
+ // reset the controller
+ usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp;
+ usbcmd->hcreset = TRUE;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+
+ for(;;)
+ {
+ // interval.QuadPart = -100 * 10000; // 10 ms
+ // KeDelayExecutionThread( KernelMode, FALSE, &interval );
+ KeStallExecutionProcessor( 10 );
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) );
+ if( !usbcmd->hcreset )
+ break;
+ }
+
+ // prepare the registers
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_CTRLDSSEGMENT ), 0 );
+
+ // turn on all the int
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBINTR ), \
+ EHCI_USBINTR_INTE | \
+ EHCI_USBINTR_ERR | \
+ EHCI_USBINTR_ASYNC | \
+ EHCI_USBINTR_HSERR
+ // EHCI_USBINTR_FLROVR | \ // it is noisy
+ // EHCI_USBINTR_PC | // we detect it by polling
+ );
+ // write the list base reg
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_PERIODICLISTBASE ), \
+ ehci->frame_list_phys_addr.LowPart );
+
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_ASYNCLISTBASE ), \
+ ehci->skel_async_qh->phys_addr & ~( 0x1f ) );
+
+ usbcmd->int_threshold = 1;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+
+ // let's rock
+ usbcmd->run_stop = 1;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+
+ // set the configuration flag
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_CONFIGFLAG ), 1 );
+
+ // enable the list traversaling
+ usbcmd->async_enable = 1;
+ usbcmd->periodic_enable = 1;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+
+ return TRUE;
+}
+
+VOID
+ehci_run_stop(
+PEHCI_DEV ehci,
+BOOL start
+)
+{
+ PEHCI_USBCMD_CONTENT usbcmd;
+ PEHCI_USBSTS_CONTENT usbsts;
+ ULONG tmp;
+ PBYTE base;
+
+ if( ehci == NULL )
+ return;
+
+ base = ehci->port_base;
+ usbcmd = ( PEHCI_USBCMD_CONTENT )&tmp;
+ usbsts = ( PEHCI_USBSTS_CONTENT )&tmp;
+
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBSTS ) );
+ if( start && usbsts->hc_halted == 0 )
+ {
+ TRAP();
+ usb_dbg_print( DBGLVL_MEDIUM, ( "ehci_run_stop(): WARNING: hc running, can not start again\n" ) );
+ return;
+ }
+ tmp = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ) );
+ usbcmd->run_stop = start ? 1 : 0;
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( base + EHCI_USBCMD ), tmp );
+ if( start )
+ usb_wait_ms_dpc( 2 ); //ehci spec 16 microframes
+}
+
+VOID
+ehci_find_min_bandwidth(
+PEHCI_DEV ehci
+)
+{
+ LONG i;
+ if( ehci == NULL )
+ return;
+
+ for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i++ )
+ {
+ ehci->min_bw = ehci->min_bw < ehci->frame_bw[ i ] ? ehci->min_bw : ehci->frame_bw[ i ] ;
+ }
+ return;
+}
+
+BOOL
+ehci_claim_bw_for_int(
+PEHCI_DEV ehci,
+PURB purb,
+BOOL release
+)
+// should have pending_endp_list_lock acquired, and purb->pipe prepared
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ LONG i, j;
+ ULONG interval, bus_time, ss_time, cs_time;
+ BOOL bw_avail;
+ ULONG max_packet_size;
+
+ if( ehci == NULL || purb == NULL )
+ return FALSE;
+
+ if( purb->pipe == 0 )
+ return FALSE;
+
+ bw_avail = FALSE;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = REAL_INTERVAL;
+ if( pipe_content->speed_high == 0 )
+ {
+ // translate to high speed uframe count
+ interval <<= 3;
+ }
+
+ if( interval > ( ehci->frame_count << 3 ) )
+ interval = ( ehci->frame_count << 3 );
+
+ max_packet_size = ( 1 << pipe_content->max_packet_size );
+ if( pipe_content->speed_high )
+ {
+ // this is a high speed endp
+
+ bus_time = usb_calc_bus_time(
+ USB_SPEED_HIGH,
+ ( pipe_content->trans_dir ) << 7,
+ FALSE,
+ min( purb->data_length, ( LONG )max_packet_size )
+ );
+
+ // multiple transactions per uframe
+ if( purb->data_length > ( LONG )max_packet_size )
+ {
+ bus_time *= ( purb->data_length ) / max_packet_size;
+ bus_time += usb_calc_bus_time(
+ USB_SPEED_HIGH,
+ ( pipe_content->trans_dir ) << 7,
+ FALSE,
+ purb->data_length % max_packet_size
+ );
+ }
+ bus_time = ( bus_time + 1 ) >> 1;
+
+ if( release )
+ {
+ for( i = pipe_content->start_uframe + ( purb->int_start_frame << 3 ); i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] += ( USHORT )bus_time;
+ }
+ goto LBL_OUT;
+ }
+ if( bus_time < ehci->min_bw )
+ {
+ // start the poll from uframe zero of each frame
+ bw_avail = TRUE;
+ pipe_content->start_uframe = 0;
+ for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )bus_time;
+ if( ehci->frame_bw[ i ] < ehci->min_bw )
+ ehci->min_bw = ehci->frame_bw[ i ];
+ }
+ }
+ else // bother to find a pattern
+ {
+ for( j = 0; j < ( LONG )interval; j++ )
+ {
+ for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ if( ehci->frame_bw[ i ] < bus_time )
+ break;
+ }
+ if( i > ( LONG )( ehci->frame_count << 3 ) )
+ {
+ bw_avail = TRUE;
+ break;
+ }
+ }
+ if( bw_avail )
+ {
+ for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )bus_time;
+ if( ehci->frame_bw[ i ] < ehci->min_bw )
+ ehci->min_bw = ehci->frame_bw[ i ];
+ }
+ pipe_content->start_uframe = j & 7;
+ purb->int_start_frame = j >> 3;
+ }
+ }
+ }
+ else // full/low speed pipe
+ {
+ // split condition is considered
+ if( pipe_content->trans_dir )
+ {
+ // an input interrupt, with handshake
+ // 55 is 144 - 90 + 1, turnaround time is one byte not the worst case 90 bytes,
+ // refer to ehci-1.0 table 4-5 p64
+ ss_time = 231 * 25 / 12;
+ // cs_time = ( 55 * 8 + ( LONG )( ( ( 19 + 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12;
+ cs_time = ( 55 * 8 + ( LONG )( ( ( 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12;
+ }
+ else
+ {
+ // according to ehci-1.0 table 4-5 p64
+ ss_time = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * purb->data_length ) / 6 ) ) ) * 25 / 12;
+ // 287 = 237 + 48( handshake ) + 8( turn around time )
+ cs_time = 287 * 25 / 12;
+ }
+ ss_time >>=1, cs_time >>=1;
+ if( release )
+ {
+ for( i = pipe_content->start_uframe + ( purb->int_start_frame << 3 ); i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] += ( USHORT )ss_time;
+ ehci->frame_bw[ i + 2 ] += ( USHORT )cs_time;
+ ehci->frame_bw[ i + 3 ] += ( USHORT )cs_time;
+ if( ( i & 0x07 ) != 0x06 )
+ ehci->frame_bw[ i + 4 ] += ( USHORT )cs_time;
+ }
+ goto LBL_OUT;
+ }
+ if( ss_time < ehci->min_bw && cs_time < ehci->min_bw )
+ {
+ pipe_content->start_uframe = 0;
+ bw_avail = TRUE;
+ for( i = 0; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )ss_time;
+ ehci->min_bw = min( ehci->frame_bw[ i ], ehci->min_bw );
+ ehci->frame_bw[ i + 2 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 2 ], ehci->min_bw );
+ ehci->frame_bw[ i + 3 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 3 ], ehci->min_bw );
+ if( ( i & 0x07 ) != 0x06 )
+ {
+ ehci->frame_bw[ i + 4 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 4 ], ehci->min_bw );
+ }
+ }
+ }
+ else
+ {
+ for( j = 0; j < ( LONG )interval; j++ )
+ {
+ if( ( j & 0x7 )== 7 ) // start-split not allowed at this uframe
+ continue;
+
+ for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ if( ehci->frame_bw[ i ] < ss_time )
+ break;
+ if( ehci->frame_bw[ i + 2 ] < cs_time )
+ break;
+ if( ehci->frame_bw[ i + 3 ] < cs_time )
+ break;
+ if( ( i & 0x7 ) != 6 )
+ if( ehci->frame_bw[ i + 4 ] < cs_time )
+ break;
+ }
+ if( i > ( LONG )( ehci->frame_count << 3 ) )
+ {
+ bw_avail = TRUE;
+ break;
+ }
+ }
+
+ pipe_content->start_uframe = j & 7;
+ purb->int_start_frame = j >> 3;
+
+ for( i = j; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )ss_time;
+ ehci->min_bw = min( ehci->frame_bw[ i ], ehci->min_bw );
+ ehci->frame_bw[ i + 2 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 2 ], ehci->min_bw );
+ ehci->frame_bw[ i + 3 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 3 ], ehci->min_bw );
+ if( ( i & 0x7 ) != 6 )
+ {
+ ehci->frame_bw[ i + 4 ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ i + 4 ], ehci->min_bw );
+ }
+ }
+ }
+ }
+
+LBL_OUT:
+ if( !release )
+ return bw_avail;
+ else
+ {
+ ehci_find_min_bandwidth( ehci );
+ return TRUE;
+ }
+}
+
+LONG
+ehci_get_cache_policy(
+PEHCI_DEV ehci
+)
+{
+ PEHCI_HCC_CONTENT hcc_content;
+
+ hcc_content = ( PEHCI_HCC_CONTENT ) &ehci->ehci_caps;
+ if( hcc_content->iso_sched_threshold >= 8 )
+ return 16;
+ if( hcc_content->iso_sched_threshold == 0 )
+ return 2;
+ return hcc_content->iso_sched_threshold + 2;
+}
+
+// 25/12 is bus-time per bit ( 1000 / 480 )
+#define BEST_BUDGET_TIME_UFRAME ( ( 188 * 7 / 6 ) * 25 / 12 )
+
+// in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet
+// out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet
+#define iso_max_data_load( dir ) ( dir == USB_DIR_IN ? \
+ ( ( 188 * 8 - 231 - 8 - 67 + ( 8 - 1 ) ) / 8 ) : ( 188 - 49 ) )
+
+#define iso_uframes_data_load( dir, data_load, uf_cnt )
+
+BOOL
+ehci_claim_bw_for_iso(
+PEHCI_DEV ehci,
+PURB purb,
+BOOL release
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ LONG i, j, k;
+ ULONG interval, bus_time, ss_time, cs_time, remainder;
+ BOOL bw_avail;
+ ULONG cur_uframe, start_uframe, max_time, max_packet_size;
+ PBYTE base;
+ if( ehci == NULL || purb == NULL )
+ return FALSE;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ base = ehci->port_base;
+ cur_uframe = EHCI_READ_PORT_ULONG( ( PULONG )( base + EHCI_FRINDEX ) ) + 1;
+ bw_avail = FALSE;
+
+ max_packet_size = purb->params[ 0 ]; //( 1 << pipe_content->max_packet_size );
+
+ if( pipe_content->speed_high )
+ {
+ interval = REAL_INTERVAL;
+ if( purb->iso_frame_count == 0 || purb->iso_frame_count + 2 * 8 > ( LONG )( ehci->frame_count << 3 ) )
+ return FALSE;
+
+ for( i = 0, max_time = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ bus_time = usb_calc_bus_time(
+ USB_SPEED_HIGH,
+ ( pipe_content->trans_dir ) << 7,
+ TRUE,
+ min( purb->iso_packet_desc[ i ].length, ( LONG )max_packet_size )
+ );
+ // NOTE: we did not use endp_mult_count here, because the comparison is enough
+ // to calculate the bandwidth
+ if( purb->iso_packet_desc[ i ].length > ( LONG )max_packet_size )
+ {
+ // multiple transactions per uframe
+ bus_time *= purb->iso_packet_desc[ i ].length / max_packet_size;
+ bus_time += usb_calc_bus_time(
+ USB_SPEED_HIGH,
+ ( pipe_content->trans_dir ) << 7,
+ TRUE,
+ purb->iso_packet_desc[ i ].length % max_packet_size
+ );
+ }
+ bus_time = ( bus_time + 1 ) >> 1;
+ max_time = max( bus_time, max_time );
+ purb->iso_packet_desc[ i ].bus_time = bus_time;
+ }
+
+ if( release )
+ {
+ // it is a release operation
+ for( i = purb->iso_start_frame, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) )
+ {
+ ehci->frame_bw[ i ] += ( USHORT )purb->iso_packet_desc[ k ].bus_time;
+ }
+ ehci_find_min_bandwidth( ehci );
+ return TRUE;
+ }
+
+ if( max_time < ehci->min_bw )
+ {
+ start_uframe = cur_uframe + ehci_get_cache_policy( ehci ); // avoid cache
+ for( i = start_uframe, j = 0; j < ( LONG )purb->iso_frame_count; i = ( i + interval ) % ( ehci->frame_count << 3 ), j++ )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )purb->iso_packet_desc[ j ].bus_time;
+ ehci->min_bw = min( ehci->frame_bw[ j ], ehci->min_bw );
+ }
+ purb->iso_start_frame = start_uframe;
+ return TRUE;
+ }
+ else // max_time >= ehci->min_bw
+ {
+ for( j = 0; j < ( LONG )interval; j++ )
+ {
+ start_uframe = cur_uframe + ehci_get_cache_policy( ehci ) + j;
+ for( i = start_uframe, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) )
+ {
+ if( ehci->frame_bw[ i ] < ( USHORT )purb->iso_packet_desc[ k ].bus_time )
+ {
+ break;
+ }
+ }
+
+ if( k < ( LONG )purb->iso_frame_count )
+ continue;
+ bw_avail = TRUE;
+ break;
+ }
+ if( bw_avail )
+ {
+ // allocate the bandwidth
+ for( i = start_uframe, k = 0; k < ( LONG )purb->iso_frame_count; k++, i = ( i + interval ) % ( ehci->frame_count << 3 ) )
+ {
+ ehci->frame_bw[ i ] -= ( USHORT )purb->iso_packet_desc[ k ].bus_time;
+ ehci->min_bw = min( ehci->min_bw, ehci->frame_bw[ i ] );
+ }
+ purb->iso_start_frame = start_uframe;
+ }
+ }
+ }
+ else // not high speed endpoint
+ {
+ // split transfer
+ if( purb->iso_frame_count == 0 || purb->iso_frame_count + 2 > ( LONG )ehci->frame_count )
+ return FALSE;
+
+ if( max_packet_size > 1023 )
+ return FALSE;
+
+ remainder = 0;
+
+ //
+ // calculate for each frame
+ // in: 231 is sum of split token + host ipg + token, 8 is bus turn-around time, 67 is full speed data token in DATA packet
+ // out: 49 byte is sum of split token+ host ipg + token + host ipg + data packet
+ // bit-stuffing is for high speed bus transfer
+ //
+
+ if( pipe_content->trans_dir )
+ {
+ // an input transfer, no handshake
+ ss_time = 231 * 25 / 12;
+ // cs_time = ( 231 + 8 + 67 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
+ cs_time = ( 231 + 8 + 67 + ( LONG )( ( ( 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
+ }
+ else
+ {
+ // an output transfer according to ehci-1.0 table 4-5 p64
+ // ss_time = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
+ ss_time = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * 188 ) / 6 ) ) ) * 25 / 12;
+ cs_time = 0;
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ // remainder = ( 49 * 8 + ( LONG )( ( ( 19 + 7 * 8 * ( purb->iso_packet_desc[ i ].length % 188 ) ) / 6 ) ) ) * 25 / 12;
+ remainder = ( 49 * 8 + ( LONG )( ( ( 7 * 8 * ( purb->iso_packet_desc[ i ].length % 188 ) ) / 6 ) ) ) * 25 / 12;
+ remainder >>= 1;
+ purb->iso_packet_desc[ i ].params.bus_time = ( USHORT )remainder;
+ }
+ }
+
+ ss_time >>= 1;
+ cs_time >>= 1;
+ cur_uframe = ( cur_uframe + 7 ) & ( ~0x07 );
+
+ j = ehci->frame_count << 3;
+ if( release )
+ {
+ if( pipe_content->trans_dir )
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ ehci->frame_bw[ purb->iso_packet_desc[ i ].params.start_uframe ] += ( USHORT )ss_time;
+ for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ )
+ {
+ ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time;
+ }
+
+ // two extra complete-split
+ ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time;
+ ehci->frame_bw[ ( cur_uframe + 0x13 + ( i << 3 ) + k ) % j ] += ( USHORT )cs_time;
+ }
+ }
+ else
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ )
+ {
+ ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) + k ) % j ] += ( USHORT )ss_time;
+ }
+ }
+ }
+ ehci_find_min_bandwidth( ehci );
+ }
+
+ // search for available bw
+ if( ss_time < ehci->min_bw && cs_time < ehci->min_bw )
+ {
+ if( pipe_content->trans_dir )
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) ) % j ] -= ( USHORT )ss_time;
+ ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) ) % j ], ehci->min_bw );
+
+ for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ )
+ {
+ ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) %j ], ehci->min_bw );
+ }
+
+ // two extra complete-split
+ ehci->frame_bw[ ( cur_uframe + 0x12 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ cur_uframe + 0x12 + ( i << 3 ) + k ], ehci->min_bw );
+ ehci->frame_bw[ ( cur_uframe + 0x13 + ( i << 3 ) + k ) % j ] -= ( USHORT )cs_time;
+ ehci->min_bw = min( ehci->frame_bw[ cur_uframe + 0x13 + ( i << 3 ) + k ], ehci->min_bw );
+ }
+ }
+ else // iso output
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ for( k = 0; k < ( purb->iso_packet_desc[ i ].length + 187 ) / 188; k++ )
+ {
+ ehci->frame_bw[ ( cur_uframe + 0x10 + ( i << 3 ) + k ) % j ] -= ( USHORT )ss_time;
+ ehci->min_bw = min( ehci->frame_bw[ ( cur_uframe + 0x11 + ( i << 3 ) + k ) %j ], ehci->min_bw );
+ }
+ }
+ }
+ purb->iso_start_frame = 0;
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ if( i == 0 )
+ purb->iso_packet_desc[ i ].params.start_uframe = ( USHORT )( cur_uframe + 0x10 );
+ else
+ purb->iso_packet_desc[ i ].params.start_uframe = purb->iso_packet_desc[ i - 1 ].params.start_uframe + 0x8;
+ }
+ bw_avail = TRUE;
+ }
+ else // take the pain to find one
+ {
+ BOOL large;
+ long temp, base;
+
+ for( j = ( cur_uframe >> 3 ) + 2; j != ( LONG )( cur_uframe >> 3 ); j = ( j + 1 ) % ehci->frame_count )
+ {
+ temp = 0;
+
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ large = purb->iso_packet_desc[ i ].length > 579;
+ base = ( purb->iso_packet_desc[ i ].length + 187 ) / 188;
+
+ if( base > 6 )
+ return FALSE;
+
+ if( pipe_content->trans_dir )
+ {
+ // input split iso, for those large than 579, schedule it at the uframe boundary
+ for( temp = 0; temp < ( large == FALSE ) ? ( 8 - base - 1 ) : 1; temp++ )
+ {
+ k = ( ( ( j + i ) << 3 ) + temp ) % ( ehci->frame_count << 3 );
+ if( ehci->frame_bw[ k ] > ss_time )
+ continue;
+
+ k = base;
+ while( k != 0 )
+ {
+ if( ehci->frame_bw[ ( ( ( j + i ) << 3 ) + 1 + temp + k ) % ( ehci->frame_count << 3 ) ] < cs_time )
+ break;
+ k--;
+ }
+ if( k > 0 ) // not available
+ continue;
+
+ // the first following extra cs
+ k = ( ( ( j + i ) << 3 ) + 2 + temp + base ) % ( ehci->frame_count << 3 );
+ if( ehci->frame_bw[ k ] < cs_time )
+ continue;
+
+ if( base < 6 )
+ {
+ // very large one does not have this second extra cs
+ if( ehci->frame_bw[ ( k + 1 ) % ( ehci->frame_count << 3 ) ] < cs_time )
+ continue;
+ }
+ }
+
+ if( temp == 8 - 1 - base ) // no bandwidth for ss
+ break;
+ }
+ else // output
+ {
+ // note: 8 - 1 - base has different meaning from the above
+ // it is to avoid the ss on H-Frame 7, but the above one is
+ // the latency of the classic bus.
+ for( temp = 0; temp < 8 - 1 - base; temp++ )
+ {
+ if( ehci->frame_bw[ ( ( j + i ) << 3 ) % ( ehci->frame_count << 3 ) + temp ] > ss_time )
+ continue;
+
+ for( k = temp; k < temp + base; k++ )
+ {
+ if( ehci->frame_bw[ ( ( j + i ) << 3 ) % ( ehci->frame_count << 3 ) + k ] < ss_time )
+ break;
+ }
+ }
+
+ if( temp == 8 - 1 - base )
+ break;
+ }
+
+ purb->iso_packet_desc[ i ].params.start_uframe = ( USHORT )( ( ( ( j + i ) << 3 ) + temp ) % ( ehci->frame_count << 3 ) );
+ // next frame
+ }
+ if( i < ( LONG )purb->iso_frame_count )
+ {
+ // skip to the next section
+ j = ( j + i ) % ( ehci->frame_count << 3 );
+ continue;
+ }
+ bw_avail = TRUE;
+ break;
+ }
+ // FIXME: Should we claim bw for the last complete split sitd? this is not done
+ // yet.
+ if( bw_avail )
+ {
+ if( pipe_content->trans_dir )
+ {
+ // input iso
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ j = purb->iso_packet_desc[ i ].length;
+ temp = ( purb->iso_packet_desc[ i ].params.start_uframe ) % ( ehci->frame_count << 3 );
+ ehci->frame_bw[ temp ] -= ( USHORT )ss_time;
+ for( k = 0; k < ( j + 187 ) / 188; j++ )
+ {
+ ehci->frame_bw[ temp + 2 + k ] -= ( USHORT )cs_time;
+ }
+ ehci->frame_bw[ temp + 2 + k ] -= ( USHORT )cs_time;
+ if( ( j + 187 ) / 188 < 6 ) //ehci restriction
+ {
+ ehci->frame_bw[ temp + 3 + k ] -= ( USHORT )cs_time;
+ }
+ }
+ }
+ else //output iso
+ {
+ for( i = 0; i < ( LONG )purb->iso_frame_count; i++ )
+ {
+ j = purb->iso_packet_desc[ i ].length;
+ temp =( purb->iso_packet_desc[ i ].params.start_uframe ) % ( ehci->frame_count << 3 );
+ for( k = 0; k < j / 188; j++ )
+ {
+ ehci->frame_bw[ temp + k ] -= ( USHORT )ss_time;
+ }
+ if( j % 188 )
+ ehci->frame_bw[ temp + k ] -= purb->iso_packet_desc[ i ].params.bus_time;
+ }
+ }
+ }
+ }
+ }
+ return bw_avail;
+}
+
+BOOL
+ehci_claim_bandwidth(
+PEHCI_DEV ehci,
+PURB purb,
+BOOL claim_bw )//true to claim band-width, false to free band-width
+// should have pending_endp_list_lock acquired, and purb->pipe prepared
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ BOOL ret;
+
+ if( ehci == NULL || purb == NULL )
+ return FALSE;
+
+ ret = FALSE;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC )
+ {
+ ret = ehci_claim_bw_for_iso( ehci, purb, claim_bw ? FALSE : TRUE );
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT )
+ {
+ ret = ehci_claim_bw_for_int( ehci, purb, claim_bw ? FALSE : TRUE );
+ }
+ else
+ TRAP();
+ return ret;
+}
+
+BOOL
+ehci_can_remove(
+PURB purb,
+BOOL door_bell_rings,
+ULONG cur_frame
+)
+// test if the purb can be removed from the schedule, by current frame index and
+// purb states
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ ULONG interval;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = REAL_INTERVAL;
+
+ switch( purb->flags & URB_FLAG_STATE_MASK )
+ {
+ case URB_FLAG_STATE_PENDING:
+ {
+ // not impossible
+ TRAP();
+ break;
+ }
+ case URB_FLAG_STATE_IN_PROCESS:
+ {
+ break;
+ }
+ case URB_FLAG_STATE_DOORBELL:
+ {
+ if( ( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) && \
+ door_bell_rings == TRUE )
+ {
+ return TRUE;
+ }
+ else if( ( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL ) )
+ {
+ break;
+ }
+ else
+ {
+ TRAP();
+ break;
+ }
+ }
+ case URB_FLAG_STATE_WAIT_FRAME:
+ {
+ // need more processing
+ if( ( purb->flags & URB_FLAG_FORCE_CANCEL ) == 0 )
+ {
+ TRAP();
+ break;
+ }
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT )
+ {
+ return door_bell_rings;
+ }
+ else // isochronous can not be canceled
+ {
+ TRAP();
+ break;
+ }
+ }
+ }
+ return FALSE;
+}
+
+NTSTATUS
+ehci_remove_urb_from_schedule(
+PEHCI_DEV ehci,
+PURB purb
+);
+
+static VOID
+ehci_deactivate_urb(
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PLIST_ENTRY pthis, pnext;
+ PEHCI_QH_CONTENT pqh_content;
+ PEHCI_QTD_CONTENT pqtd_content;
+
+ if( purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ switch( pipe_content->trans_type )
+ {
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ {
+ ListFirst( &purb->trasac_list, pthis );
+ pqh_content = ( PEHCI_QH_CONTENT )qh_from_list_entry( pthis );
+ ListNext( &purb->trasac_list, pthis, pnext );
+ do
+ {
+ pqtd_content = ( PEHCI_QTD_CONTENT )qtd_from_list_entry( pthis );
+ if( pqtd_content->status & QTD_STS_ACTIVE )
+ {
+ pqtd_content->status &= ~QTD_STS_ACTIVE;
+ }
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+
+ }while( pthis );
+ break;
+ }
+ case USB_ENDPOINT_XFER_ISOC:
+ {
+ // fall through
+ }
+ default:
+ TRAP();
+ }
+ return;
+}
+
+static VOID
+ehci_insert_bulk_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+// list head is only a handle, the qh and qtd are following it.
+{
+ PLIST_ENTRY list_head;
+ PEHCI_QH pqh, pqhprev, pqhnext;
+ PEHCI_QTD ptd;
+ PLIST_ENTRY pthis, pprev, pnext;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ list_head = &purb->trasac_list;
+ ListFirst( list_head, pthis );
+ if( pthis == NULL )
+ return;
+
+ if( elem_type_list_entry( pthis ) != INIT_LIST_FLAG_QH )
+ return;
+
+ pqh = qh_from_list_entry( pthis );
+ // the last qh
+ ListFirstPrev( &ehci->async_list_cpu, pprev );
+ pqhprev = qh_from_schedule( pprev );
+
+ // the first qh
+ ListFirst( &ehci->async_list_cpu, pnext );
+ pqhnext = qh_from_schedule( pnext );
+
+ if( pprev == &ehci->async_list_cpu )
+ {
+ // always a qh in async list
+ TRAP();
+ return;
+ }
+ pqh->hw_next = pqhnext->phys_addr;
+ InsertTailList( &ehci->async_list_cpu, &pqh->elem_head_link->sched_link );
+ pqhprev->hw_next = pqh->phys_addr;
+ return;
+}
+
+static VOID
+ehci_remove_bulk_from_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+// executed in isr, and have frame_list_lock acquired, so
+// never try to acquire any spin-lock
+// remove the bulk purb from schedule, and mark it not in
+// the schedule
+{
+ PLIST_ENTRY list_head;
+ PEHCI_QH pqh, pqhprev, pqhnext;
+ PEHCI_QTD ptd;
+ PEHCI_QH_CONTENT pqhc;
+ PLIST_ENTRY pthis, pprev, pnext;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ list_head = &purb->trasac_list;
+ ListFirst( list_head, pthis );
+ if( pthis == NULL )
+ {
+ TRAP();
+ return;
+ }
+ pqh = qh_from_list_entry( pthis );
+ pqhc = ( PEHCI_QH_CONTENT) pqh;
+
+ if( pqhc->is_async_head )
+ TRAP();
+
+ ListFirst( &pqh->elem_head_link->sched_link, pnext );
+ ListFirstPrev( &pqh->elem_head_link->sched_link, pprev );
+
+ if( pprev == &ehci->async_list_cpu )
+ {
+ // we will at least have a qh with H-bit 1 in the async-list
+ TRAP();
+ }
+ else if( pnext == &ehci->async_list_cpu )
+ {
+ // remove the last one
+ pqhprev = qh_from_schedule( pprev );
+ ListFirst( &ehci->async_list_cpu, pnext );
+ pqhnext = qh_from_schedule( pnext );
+ pqhprev->hw_next = pqhnext->phys_addr;
+ }
+ else
+ {
+ pqhprev = qh_from_schedule( pprev );
+ pqhnext = qh_from_schedule( pnext );
+ pqhprev->hw_next = pqhnext->phys_addr;
+ }
+ RemoveEntryList( &pqh->elem_head_link->sched_link );
+ return;
+}
+
+
+static VOID
+ehci_insert_fstn_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+
+ PURB_HS_PIPE_CONTENT pipe_content, pc;
+ PLIST_ENTRY pthis, list_head, pnext, pprev;
+ PEHCI_ELEM_LINKS elem_link;
+ PEHCI_QH pqh, pqhprev, pqhnext;
+ PEHCI_FSTN pfstn;
+ PURB purb1;
+
+ ULONG interval, start_frame, start_uframe;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = ( 1 << ( pipe_content->interval + 3 ) );
+ list_head = &purb->trasac_list;
+ start_frame = purb->int_start_frame;
+ start_uframe = ( start_frame << 3 ) + 1; //( start_frame << 3 ) + pipe_content->start_uframe;
+
+ if( ( start_frame << 3 ) >= interval )
+ TRAP();
+
+ ListFirstPrev( list_head, pprev );
+
+ if( elem_type_list_entry( pprev ) != INIT_LIST_FLAG_FSTN )
+ {
+ TRAP();
+ return;
+ }
+
+ pfstn = fstn_from_list_entry( pprev );
+
+ if( interval == 8 )
+ {
+ ListFirst( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis );
+
+ // skip the first one
+ ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+
+ while( pthis )
+ {
+ purb1 = qh_from_schedule( pthis )->elem_head_link->purb;
+ pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe;
+ if( pc->speed_high )
+ {
+ TRAP();
+ return;
+ }
+ if( ( 1 << ( pc->interval + 3 ) ) > ( LONG )interval )
+ {
+ TRAP();
+ continue;
+ }
+ else if( ( 1 << ( pc->interval + 3 ) < ( LONG )interval ) )
+ {
+ break;
+ }
+ else if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN )
+ {
+ ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ }
+ else if( pc->start_uframe <= 1 )
+ {
+ ListNext( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ }
+ break;
+ }
+ if( pprev == NULL )
+ {
+ TRAP();
+ return;
+ }
+ if( pthis == NULL )
+ {
+ //the last one
+ InsertTailList( &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ], \
+ &pfstn->elem_head_link->sched_link );
+ }
+ else
+ {
+ if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN )
+ {
+ InsertHeadList( &fstn_from_schedule( pprev )->elem_head_link->sched_link, \
+ &pfstn->elem_head_link->sched_link );
+ }
+ else
+ {
+ InsertHeadList( &qh_from_schedule( pprev )->elem_head_link->sched_link, \
+ &pfstn->elem_head_link->sched_link );
+ }
+ }
+ pfstn->hw_next = qh_from_schedule( pprev )->hw_next;
+ qh_from_schedule( pprev )->hw_next = pfstn->phys_addr;
+ }
+ else
+ {
+ start_frame++;
+ for( i = start_frame; i < ( LONG )start_frame + 1; i += ( interval >> 3 ) )
+ {
+ list_head = &ehci->frame_list_cpu[ i ].td_link;
+ ListFirst( list_head, pthis );
+
+ pprev = list_head;
+ while( pthis )
+ {
+ // skip itds and sitds
+ if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_ITD ||
+ elem_type( pthis, FALSE ) == INIT_LIST_FLAG_SITD )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ break;
+ }
+
+ while( pthis )
+ {
+ // find the insertion point
+ ULONG u;
+
+ pqhnext = qh_from_schedule( pthis );
+ if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN )
+ purb1 = fstn_from_schedule( pthis )->elem_head_link->purb;
+ else
+ purb1 = pqhnext->elem_head_link->purb;
+
+ if( purb1 == NULL )
+ TRAP();
+
+ pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe;
+ u = 1 << ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) );
+
+ if( u > interval )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ else if( u == interval )
+ {
+ if( start_uframe >= \
+ ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \
+ 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ else
+ break;
+ }
+ else if( u < interval )
+ {
+ break;
+ }
+ }
+
+ if( pprev == list_head )
+ {
+ // insert to the list head
+ pnext = pfstn->elem_head_link->sched_link.Flink = list_head->Flink;
+ list_head->Flink = &pfstn->elem_head_link->sched_link;
+ pfstn->hw_next = ehci->frame_list[ i ]; // point to following node
+ ehci->frame_list[ i ] = pfstn->phys_addr;
+ }
+ else
+ {
+ pnext = pfstn->elem_head_link->sched_link.Flink = pprev->Flink;
+ pprev->Flink = &pfstn->elem_head_link->sched_link;
+
+ // fstn can be handled correctly
+ pfstn->hw_next = qh_from_schedule( pprev )->hw_next;
+ qh_from_schedule( pprev )->hw_next = pfstn->phys_addr;
+ }
+ }
+ // the pointer to next node of this fstn is alway same across the frame list.
+ for( i = start_frame + ( interval >> 3 ); i < ( LONG )ehci->frame_count; i += ( interval >> 3 ) )
+ {
+ pprev = list_head = &ehci->frame_list_cpu[ i ].td_link;
+ ListFirst( list_head, pthis );
+
+ while( pthis )
+ {
+ if( pthis == pnext )
+ {
+ break;
+ }
+ pprev = pthis;
+ ListNext( list_head, pthis, pthis );
+ }
+
+ pprev->Flink = &pfstn->elem_head_link->sched_link;
+ if( pprev == list_head )
+ ehci->frame_list[ i ] = pfstn->phys_addr;
+ else
+ qh_from_schedule( pprev )->hw_next = pfstn->phys_addr;
+ }
+ }
+}
+
+static VOID
+ehci_remove_fstn_from_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PLIST_ENTRY pthis, list_head, pnext, pprev;
+ PEHCI_FSTN pfstn;
+ PURB purb1;
+
+ ULONG interval, start_frame, start_uframe;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = ( 1 << ( pipe_content->interval + 3 ) );
+ list_head = &purb->trasac_list;
+ start_frame = purb->int_start_frame;
+ start_uframe = 1;
+
+ if( ( start_frame << 3 ) >= interval )
+ TRAP();
+ start_frame++;
+
+ ListFirstPrev( list_head, pprev );
+ if( elem_type_list_entry( pprev ) != INIT_LIST_FLAG_FSTN )
+ {
+ TRAP();
+ return;
+ }
+
+ pfstn = fstn_from_list_entry( pprev );
+ if( interval < 8 )
+ {
+ TRAP();
+ return;
+ }
+ if( interval == 8 )
+ {
+ ListFirstPrev( &pfstn->elem_head_link->sched_link, pprev );
+ qh_from_schedule( pprev )->hw_next = pfstn->hw_next;
+ RemoveEntryList( &pfstn->elem_head_link->sched_link );
+ }
+ else
+ {
+ for( i = start_frame; i < ( LONG )ehci->frame_count; i++ )
+ {
+ ListFirst( &ehci->frame_list_cpu[ i ].td_link, pthis );
+ if( pthis == NULL )
+ {
+ TRAP();
+ return;
+ }
+ pprev = &ehci->frame_list_cpu[ i ].td_link;
+ while( pthis && pthis != &pfstn->elem_head_link->sched_link )
+ {
+ pprev = pthis;
+ ListNext( &ehci->frame_list_cpu[ i ].td_link, pthis, pnext );
+ pthis = pnext;
+ }
+ if( pthis == NULL )
+ {
+ TRAP();
+ return;
+ }
+ qh_from_schedule( pprev )->hw_next = pfstn->hw_next;
+ pprev->Flink = pfstn->elem_head_link->sched_link.Flink;
+ }
+ }
+ return;
+}
+
+static VOID
+ehci_insert_int_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content, pc;
+ PLIST_ENTRY pthis, list_head, pnext, pprev;
+ PEHCI_ELEM_LINKS elem_link;
+ PEHCI_QH pqh, pqhprev, pqhnext;
+ PURB purb1;
+
+ ULONG interval, u, start_frame, start_uframe;
+ LONG i;
+ UCHAR need_fstn;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = REAL_INTERVAL;
+ start_uframe = ( purb->int_start_frame << 3 ) + pipe_content->start_uframe;
+ start_frame = purb->int_start_frame;
+ need_fstn = FALSE;
+ list_head = &purb->trasac_list;
+
+ ListFirst( list_head, pthis );
+ if( pthis == NULL )
+ return;
+
+ pqh = qh_from_list_entry( pthis );
+
+ if( !pipe_content->speed_high )
+ {
+ interval = ( interval << 3 );
+ ListFirstPrev( list_head, pprev );
+ if( elem_type_list_entry( pprev ) == INIT_LIST_FLAG_FSTN )
+ need_fstn = TRUE;
+ }
+
+ if( interval < 16 )
+ {
+ pqhprev = pqhnext = NULL;
+ if( interval == 1 )
+ {
+ list_head = &ehci->periodic_list_cpu[ EHCI_SCHED_FSTN_INDEX ];
+ ListFirst( list_head, pthis );
+ InsertTailList( list_head, &pqh->elem_head_link->sched_link );
+ ListFirstPrev( &pqh->elem_head_link->sched_link, pprev );
+ pqh->hw_next = EHCI_PTR_TERM;
+ if( pprev == pthis )
+ {
+ fstn_from_schedule( pthis )->hw_next = pqh->phys_addr;
+ }
+ else
+ {
+ qh_from_schedule( pthis )->hw_next = pqh->phys_addr;
+ }
+ }
+ else // interval == 2 or 4 or 8
+ {
+ list_head = &ehci->periodic_list_cpu[ EHCI_SCHED_INT8_INDEX ];
+ ListFirst( list_head, pthis );
+ pprev = NULL;
+ while( pthis )
+ {
+ elem_link = struct_ptr( pthis, EHCI_ELEM_LINKS, sched_link );
+ purb1 = elem_link->purb;
+ pc = ( PURB_HS_PIPE_CONTENT )purb1->pipe;
+ u = ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) );
+
+ if( interval < u )
+ {
+ ListFirstPrev( pthis, pprev );
+ break;
+ }
+ else if( interval > u )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ // FIXME: is this right to fix fstn's start_uf 1???
+ else if( start_uframe <= \
+ ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \
+ 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ else // interval is equal, and start_uframe is greater
+ {
+ ListFirstPrev( pthis, pprev );
+ break;
+ }
+ }
+ if( pprev == NULL )
+ {
+ // at least one dummy qh is there
+ TRAP();
+ }
+ else if( pnext == NULL )
+ {
+ // the last one in this chain, fstn can be handled correctly
+ InsertTailList( list_head, &pqh->elem_head_link->sched_link );
+ pqhprev = qh_from_schedule( pprev );
+ pqh->hw_next = pqhprev->hw_next;
+ pqhprev->hw_next = pqh->phys_addr;
+ }
+ else
+ { pqhprev = qh_from_schedule( pprev );
+ if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_QH )
+ {
+ InsertHeadList( &pqhprev->elem_head_link->sched_link, &pqh->elem_head_link->sched_link );
+ }
+ else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN )
+ {
+ InsertHeadList( &fstn_from_schedule( pprev )->elem_head_link->sched_link, &pqh->elem_head_link->sched_link );
+ }
+ pqh->hw_next = pqhprev->hw_next;
+ pqhprev->hw_next = pqh->phys_addr;
+ }
+ }
+ }
+ else // interval >= 16
+ {
+ if( ( start_frame << 3 ) >= interval )
+ TRAP();
+
+ for( i = start_frame; i < ( LONG )start_frame + 1; i += ( interval >> 3 ) )
+ {
+ list_head = &ehci->frame_list_cpu[ i ].td_link;
+ ListFirst( list_head, pthis );
+
+ pprev = list_head;
+ while( pthis )
+ {
+ // skip itds and sitds
+ if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_ITD ||
+ elem_type( pthis, FALSE ) == INIT_LIST_FLAG_SITD )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ break;
+ }
+
+ while( pthis )
+ {
+ // find the insertion point
+
+ pqhnext = qh_from_schedule( pthis );
+ if( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN )
+ purb1 = fstn_from_schedule( pthis )->elem_head_link->purb;
+ else
+ purb1 = pqhnext->elem_head_link->purb;
+
+ if( purb1 == NULL )
+ TRAP();
+
+ pc = ( PURB_HS_PIPE_CONTENT )&purb1->pipe;
+ u = 1 << ( pc->speed_high ? ( 1 << pc->interval ) : ( 1 << ( pc->interval + 3 ) ) );
+
+ if( u > interval )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ else if( u == interval )
+ {
+ if( start_uframe >= \
+ ( elem_type( pthis, FALSE ) == INIT_LIST_FLAG_FSTN ? \
+ 1 : pc->start_uframe ) + ( purb1->int_start_frame << 3 ) )
+ {
+ ListNext( list_head, pthis, pnext );
+ pprev = pthis;
+ pthis = pnext;
+ continue;
+ }
+ else
+ break;
+ }
+ else if( u < interval )
+ {
+ break;
+ }
+ }
+ if( pprev == list_head )
+ {
+ // insert to the list head
+ pnext = pqh->elem_head_link->sched_link.Flink = list_head->Flink;
+ list_head->Flink = &pqh->elem_head_link->sched_link;
+ pqh->hw_next = ehci->frame_list[ i ]; // point to following node
+ ehci->frame_list[ i ] = pqh->phys_addr;
+ }
+ else
+ {
+ pnext = pqh->elem_head_link->sched_link.Flink = pprev->Flink;
+ pprev->Flink = &pqh->elem_head_link->sched_link;
+
+ // fstn can be handled correctly
+ pqh->hw_next = qh_from_schedule( pprev )->hw_next;
+ qh_from_schedule( pprev )->hw_next = pqh->phys_addr;
+ }
+ }
+ for( i = start_frame + ( interval >> 3 ); i < ( LONG )ehci->frame_count; i += ( interval >> 3 ) )
+ {
+ pprev = list_head = &ehci->frame_list_cpu[ i ].td_link;
+ ListFirst( list_head, pthis );
+
+ while( pthis )
+ {
+ if( pthis == pnext )
+ {
+ break;
+ }
+ pprev = pthis;
+ ListNext( list_head, pthis, pthis );
+ }
+
+ pprev->Flink = &pqh->elem_head_link->sched_link;
+ if( pprev == list_head )
+ ehci->frame_list[ i ] = pqh->phys_addr;
+ else
+ qh_from_schedule( pprev )->hw_next = pqh->phys_addr;
+ }
+ }
+
+ if( need_fstn )
+ ehci_insert_fstn_schedule( ehci, purb );
+
+ return;
+}
+
+static VOID
+ehci_remove_int_from_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content, pc;
+ PLIST_ENTRY pthis, list_head, pnext, pprev, pcur;
+ PEHCI_ELEM_LINKS elem_link;
+ PEHCI_QH pqh, pqhprev, pqhnext;
+ PURB purb1;
+
+ ULONG interval, start_frame, start_uframe, u;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ interval = REAL_INTERVAL;
+ start_uframe = ( purb->int_start_frame << 3 ) + pipe_content->start_uframe;
+ start_frame = purb->int_start_frame;
+
+ ListFirst( &purb->trasac_list, pthis );
+ if( pthis == NULL )
+ return;
+
+ pqh = qh_from_list_entry( pthis );
+ list_head = &purb->trasac_list;
+
+ if( IsListEmpty( list_head ) )
+ {
+ TRAP();
+ return;
+ }
+
+ if( !pipe_content->speed_high )
+ {
+ interval = ( interval << 3 );
+ }
+
+ if( interval >= 256 * 8 )
+ return;
+
+ if( interval < 16 )
+ {
+ ListFirstPrev( &pqh->elem_head_link->sched_link, pprev );
+ RemoveEntryList( &pqh->elem_head_link->sched_link );
+ if( interval > 1 )
+ {
+ pqhprev = qh_from_schedule( pprev );
+ pqhprev->hw_next = pqh->hw_next;
+ }
+ else
+ {
+ ListFirst( &ehci->frame_list_cpu[ EHCI_SCHED_FSTN_INDEX ].td_link, list_head );
+ if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN )
+ {
+ fstn_from_schedule( list_head )->hw_next = pqh->hw_next;
+ }
+ else
+ {
+ qh_from_schedule( pprev )->hw_next = pqh->hw_next;
+ }
+ }
+ }
+ else if( interval >= 16 )
+ {
+ ListFirst( list_head, pthis );
+ pthis = &pqh->elem_head_link->sched_link;
+
+ for( i = start_uframe; i < ( LONG )( ehci->frame_count << 3 ); i += interval )
+ {
+ ListFirst( &ehci->frame_list_cpu[ i ].td_link, pcur );
+ pprev = NULL;
+ while( pthis != pcur && pcur )
+ {
+ ListNext( &ehci->frame_list_cpu[ i ].td_link, pcur, pnext );
+ pprev = pcur;
+ pcur = pnext;
+ }
+
+ if( pcur == NULL )
+ {
+ TRAP();
+ continue;
+ }
+ else if( pprev == NULL )
+ {
+ // the first one in the frame list
+ ehci->frame_list_cpu[ i ].td_link.Flink = pthis->Flink;
+ ehci->frame_list[ i ] = qh_from_schedule( pthis )->hw_next;
+ }
+ else
+ {
+ if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_QH )
+ {
+ qh_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink;
+ qh_from_schedule( pprev )->hw_next = pqh->hw_next;
+ }
+ else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_ITD )
+ {
+ itd_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink;
+ itd_from_schedule( pprev )->hw_next = pqh->hw_next;
+ }
+ else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_SITD )
+ {
+ sitd_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink;
+ sitd_from_schedule( pprev )->hw_next = pqh->hw_next;
+ }
+ else if( elem_type( pprev, FALSE ) == INIT_LIST_FLAG_FSTN )
+ {
+ fstn_from_schedule( pprev )->elem_head_link->sched_link.Flink = pqh->elem_head_link->sched_link.Flink;
+ fstn_from_schedule( pprev )->hw_next = pqh->hw_next;
+ }
+ else
+ TRAP();
+ }
+ }
+ }
+
+ ListFirstPrev( &purb->trasac_list, pprev );
+ if( elem_type_list_entry( pprev ) == INIT_LIST_FLAG_FSTN )
+ ehci_remove_fstn_from_schedule( ehci, purb );
+ return;
+}
+
+static VOID
+ehci_insert_iso_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PLIST_ENTRY pthis, list_head, pnext, pprev, pcur;
+
+ ULONG interval, start_frame;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+
+ interval = 8;
+ if( pipe_content->speed_high )
+ interval = REAL_INTERVAL;
+
+ start_frame = purb->iso_start_frame;
+
+ ListFirst( &purb->trasac_list, pthis );
+ if( pthis == NULL )
+ {
+ TRAP();
+ return;
+ }
+
+ list_head = &purb->trasac_list;
+ if( IsListEmpty( list_head ) )
+ {
+ TRAP();
+ return;
+ }
+
+ i = start_frame;
+ while( pthis )
+ {
+ if( pipe_content->speed_high )
+ {
+ itd_from_list_entry( pthis )->elem_head_link->sched_link.Flink = ehci->frame_list_cpu[ i ].td_link.Flink;
+ itd_from_list_entry( pthis )->hw_next = ehci->frame_list[ i ];
+
+ ehci->frame_list[ i ] = itd_from_list_entry( pthis )->phys_addr;
+ ehci->frame_list_cpu[ i ].td_link.Flink = pthis;
+ }
+ else
+ {
+ sitd_from_list_entry( pthis )->elem_head_link->sched_link.Flink = ehci->frame_list_cpu[ i ].td_link.Flink;
+ sitd_from_list_entry( pthis )->hw_next = ehci->frame_list[ i ];
+
+ ehci->frame_list[ i ] = sitd_from_list_entry( pthis )->phys_addr;
+ ehci->frame_list_cpu[ i ].td_link.Flink = pthis;
+ }
+
+ ListNext( list_head, pthis, pnext );
+ pthis = pnext;
+
+ if( interval <= 8 )
+ i++;
+ else
+ i += ( interval >> 3 );
+ }
+ return;
+}
+
+static VOID
+ehci_remove_iso_from_schedule(
+PEHCI_DEV ehci,
+PURB purb
+)
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PLIST_ENTRY pthis, list_head, pnext, pprev, pcur;
+
+ ULONG interval, start_frame;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+
+ interval = 8;
+ if( pipe_content->speed_high )
+ interval = REAL_INTERVAL;
+
+ start_frame = purb->iso_start_frame;
+
+ ListFirst( &purb->trasac_list, pthis );
+ if( pthis == NULL )
+ {
+ TRAP();
+ return;
+ }
+
+ list_head = &purb->trasac_list;
+ if( IsListEmpty( list_head ) )
+ {
+ TRAP();
+ return;
+ }
+
+ i = start_frame;
+ while( pthis )
+ {
+ // for the possible existance of sitd back pointer, we can not use for(...)
+ ListFirst( &ehci->frame_list_cpu[ i ].td_link, pcur );
+ pprev = &ehci->frame_list_cpu[ i ].td_link;
+ while( pcur )
+ {
+ if( pcur != pthis )
+ {
+ ListNext( &ehci->frame_list_cpu[ i ].td_link, pcur, pnext );
+ pprev = pcur;
+ pcur = pnext;
+ continue;
+ }
+ break;
+ }
+
+ if( pcur == NULL )
+ {
+ TRAP();
+ }
+ pprev->Flink = pcur->Flink;
+ if( pprev != &ehci->frame_list_cpu[ i ].td_link )
+ qh_from_schedule( pprev )->hw_next = qh_from_schedule( pcur )->hw_next;
+ else
+ ehci->frame_list[ i ] = qh_from_schedule( pcur )->hw_next;
+
+ ListNext( list_head, pthis, pnext );
+ pthis = pnext;
+
+ if( interval <= 8 )
+ i++;
+ else
+ i += ( interval >> 3 );
+ }
+ return;
+}
+
+NTSTATUS
+ehci_isr_removing_urb(
+PEHCI_DEV ehci,
+PURB purb,
+BOOL doorbell_rings,
+ULONG cur_frame
+)
+{
+ UCHAR type;
+ PLIST_ENTRY pthis, plast;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PEHCI_ITD_CONTENT pitd_content;
+ PEHCI_SITD_CONTENT psitd_content;
+ PEHCI_QH_CONTENT pqh_content;
+ PEHCI_QTD_CONTENT pqtd_content;
+ LONG i;
+
+ if( purb == NULL || ehci == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( ( purb->flags & URB_FLAG_STATE_MASK ) == URB_FLAG_STATE_FINISHED )
+ return STATUS_SUCCESS;
+
+ type = 0;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+
+ switch( purb->flags & URB_FLAG_STATE_MASK )
+ {
+ case URB_FLAG_STATE_IN_PROCESS:
+ {
+ // determine the removal type: complete, error or cancel
+ ListFirst( &purb->trasac_list, pthis );
+ if( purb->flags & URB_FLAG_FORCE_CANCEL )
+ {
+ type = 3;
+ }
+ else
+ {
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_INT || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL )
+ {
+ pqh_content = ( PEHCI_QH_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK );
+ if( EHCI_QH_ERROR( pqh_content ) )
+ {
+ purb->status = pqh_content->cur_qtd.status;
+ type = 2;
+ }
+ else
+ {
+ pqtd_content = &pqh_content->cur_qtd;
+ if( pqtd_content->terminal && \
+ ( ( pqtd_content->status & QTD_STS_ACTIVE ) == 0 ) )
+ {
+ type = 1;
+ }
+ // else, not finished
+ }
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC )
+ {
+ // FIXME: do we need to check if current frame falls out of the
+ // frame range of iso transfer
+ // inspect the last td to determine if finished
+ ListFirstPrev( &purb->trasac_list, pthis );
+ if( pthis )
+ {
+ if( pipe_content->speed_high )
+ {
+ pitd_content = ( PEHCI_ITD_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK );
+ for( i = 0; i < 8; i++ )
+ {
+ if( pitd_content->status_slot[ i ].trans_length && \
+ pitd_content->status_slot[ i ].status & 0x08 )
+ {
+ break;
+ }
+ }
+ if( i == 8 )
+ {
+ // the itds are all inactive
+ type = 1;
+ }
+ }
+ else
+ {
+ psitd_content = ( PEHCI_SITD_CONTENT )( ( ULONG )struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link )->phys_part & PHYS_PART_ADDR_MASK );
+ if( ( psitd_content->status & 0x80 ) == 0 )
+ {
+ type = 1;
+ }
+ }
+ }
+ else // empty transaction list in purb
+ TRAP();
+ }
+ else // unknown transfer type
+ TRAP();
+
+ } // end of not force cancel
+
+ if( type == 0 )
+ return STATUS_SUCCESS;
+
+ switch( type )
+ {
+ case 1:
+ {
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_BULK )
+ {
+ ehci_remove_bulk_from_schedule( ehci, purb );
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_DOORBELL;
+ purb->status = 0;
+ press_doorbell( ehci );
+ return STATUS_SUCCESS;
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_ISOC )
+ {
+ ehci_remove_iso_from_schedule( ehci, purb );
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT )
+ {
+ ehci_remove_int_from_schedule( ehci, purb );
+ }
+ else // unknown transfer type
+ TRAP();
+
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_FINISHED;
+
+ // notify dpc the purb can be completed;
+ purb->flags &= ~URB_FLAG_IN_SCHEDULE;
+ purb->status = 0;
+
+ return STATUS_SUCCESS;
+ }
+ case 2:
+ {
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_BULK )
+ {
+ ehci_deactivate_urb( purb );
+ ehci_remove_bulk_from_schedule( ehci, purb );
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_DOORBELL;
+ press_doorbell( ehci );
+ }
+ else if( pipe_content->trans_type == USB_ENDPOINT_XFER_INT )
+ {
+ ehci_remove_int_from_schedule( ehci, purb );
+
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_FINISHED;
+ purb->flags &= ~URB_FLAG_IN_SCHEDULE;
+ }
+ else // unknown transfer or iso transfer
+ TRAP();
+ return STATUS_SUCCESS;
+ }
+ case 3:
+ {
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_INT )
+ {
+ ehci_deactivate_urb( purb );
+ if( pipe_content->trans_type == USB_ENDPOINT_XFER_BULK || \
+ pipe_content->trans_type == USB_ENDPOINT_XFER_CONTROL )
+ ehci_remove_bulk_from_schedule( ehci, purb );
+ else
+ ehci_remove_int_from_schedule( ehci, purb );
+
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_DOORBELL;
+
+ press_doorbell( ehci );
+
+ }
+ else // unknown transfer or iso transfer
+ DO_NOTHING;
+ purb->status = 0;
+ return STATUS_SUCCESS;
+ }
+ default:
+ TRAP();
+ }
+ }
+ case URB_FLAG_STATE_DOORBELL:
+ {
+ if( doorbell_rings == FALSE )
+ return STATUS_SUCCESS;
+
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_FINISHED;
+ purb->flags &= ~URB_FLAG_IN_SCHEDULE;
+ return STATUS_SUCCESS;
+ }
+ }
+ return STATUS_SUCCESS;
+}
+
+static ULONG
+ehci_scan_iso_error(
+PEHCI_DEV ehci,
+PURB purb
+)
+// we only report the first error of the ITDs, purb->status is the status code
+// return the raw status for ehci_set_error_code
+{
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PEHCI_SITD_CONTENT psitd_content;
+ PEHCI_ITD_CONTENT pitd_content;
+ PLIST_ENTRY pthis, pnext;
+ LONG i;
+
+ if( ehci == NULL || purb == NULL )
+ return 0;
+
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->pipe;
+ if( pipe_content->trans_type != USB_ENDPOINT_XFER_ISOC )
+ {
+ return 0;
+ }
+
+ ListFirst( &purb->trasac_list, pthis );
+ if( pipe_content->speed_high )
+ {
+ while( pthis )
+ {
+ pitd_content = ( PEHCI_ITD_CONTENT )itd_from_list_entry( pthis );
+ for( i = 0; i < 8; i++ )
+ {
+ if( pitd_content->status_slot[ i ].status & ITD_ANY_ERROR )
+ break;
+ }
+ if( i < 8 )
+ {
+ // error occured
+ return purb->status = pitd_content->status_slot[ i ].status;
+ }
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+ }
+ else
+ {
+ while( pthis )
+ {
+ psitd_content = ( PEHCI_SITD_CONTENT )sitd_from_list_entry( pthis );
+ if( psitd_content->status & SITD_ANY_ERROR )
+ {
+ // error occured
+ if( psitd_content->s_mask == 0x04 && \
+ psitd_content->c_mask == 0x70 && \
+ psitd_content->bytes_to_transfer == 1 )
+ return purb->status = 0;
+
+ return purb->status = psitd_content->status;
+ }
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+ }
+ return 0;
+}
+
+BOOLEAN
+ehci_isr(
+PKINTERRUPT interrupt,
+PVOID context
+)
+ // we can not use endp here for it is within the dev scope, and
+ // we can not acquire the dev-lock, fortunately we saved some
+ // info in purb->pipe in ehci_internal_submit_XXX.
+{
+
+ PEHCI_DEV ehci;
+ ULONG status, urb_count;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ BOOL door_bell_rings;
+ ULONG cur_frame;
+ /*
+ * Read the interrupt status, and write it back to clear the
+ * interrupt cause
+ */
+ ehci = ( PEHCI_DEV )context;
+ if( ehci == NULL )
+ return FALSE;
+
+ status = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_USBSTS ) );
+ cur_frame = EHCI_READ_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_FRINDEX ) );
+
+ status &= ( EHCI_ERROR_INT | STS_INT | STS_IAA );
+ if( !status ) /* shared interrupt, not mine */
+ {
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): not our int\n" ) );
+ return FALSE;
+ }
+
+ /* clear it*/
+ EHCI_WRITE_PORT_ULONG( ( PULONG )( ehci->port_base + EHCI_USBSTS ), status );
+
+ if( status & EHCI_ERROR_INT )
+ {
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): current ehci status=0x%x\n", status ) );
+ }
+ else
+ {
+ ehci_dbg_print( DBGLVL_MAXIMUM, ( "ehci_isr(): congratulations, no error occurs\n" ) );
+ }
+
+
+ if ( status & STS_FATAL )
+ {
+ DbgPrint( "ehci_isr(): host system error, PCI problems?\n");
+ for( ; ; );
+
+ }
+
+ if ( status & STS_HALT ) //&& !ehci->is_suspended
+ {
+ DbgPrint( "ehci_isr(): host controller halted. very bad\n");
+ /* FIXME: Reset the controller, fix the offending TD */
+ // reset is performed in dpc
+ }
+
+
+ door_bell_rings = ( ( status & STS_IAA ) != 0 );
+
+ // scan to remove those due
+#ifdef DBG
+ urb_count = dbg_count_list( &ehci->urb_list );
+ ehci_dbg_print( DBGLVL_MAXIMUM, ("ehci_isr(): urb# in process is %d\n", urb_count ) );
+#endif
+
+ ListFirst( &ehci->urb_list, pthis );
+ while( pthis )
+ {
+ purb = ( PURB )pthis;
+ ehci_isr_removing_urb( ehci, purb, door_bell_rings, cur_frame );
+ ListNext( &ehci->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ KeInsertQueueDpc( &ehci->pdev_ext->ehci_dpc, ( PVOID )status, 0 );
+ return TRUE;
+}
+
+#ifndef INCLUDE_EHCI
+VOID
+ehci_unload(
+IN PDRIVER_OBJECT DriverObject
+)
+{
+ PDEVICE_OBJECT pdev;
+ PEHCI_DEVICE_EXTENSION pdev_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+ LONG i;
+
+ pdev = DriverObject->DeviceObject;
+
+ if( pdev == NULL )
+ return;
+
+ pdev_ext = pdev->DeviceExtension;
+ if( pdev_ext == NULL )
+ return;
+
+ dev_mgr = &g_dev_mgr;
+ if( dev_mgr == NULL )
+ return;
+ //
+ // set the termination flag
+ //
+ dev_mgr->term_flag = TRUE;
+ //
+ // wake up the thread if it is
+ //
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ KeWaitForSingleObject(
+ dev_mgr->pthread,
+ Executive,
+ KernelMode,
+ TRUE,
+ NULL);
+ ObDereferenceObject( dev_mgr->pthread );
+ dev_mgr->pthread = NULL;
+
+ dev_mgr_release_hcd( dev_mgr );
+ return;
+}
+
+NTSTATUS
+generic_dispatch_irp(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ PDEVEXT_HEADER dev_ext;
+
+ dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+
+ if( dev_ext && dev_ext->dispatch )
+ return dev_ext->dispatch( dev_obj, irp );
+
+ irp->IoStatus.Information = 0;
+
+ EXIT_DISPATCH( STATUS_UNSUCCESSFUL, irp );
+}
+
+VOID
+generic_start_io(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ PDEVEXT_HEADER dev_ext;
+
+ KIRQL old_irql;
+
+ IoAcquireCancelSpinLock( &old_irql );
+ if (irp != dev_obj->CurrentIrp || irp->Cancel)
+ {
+ IoReleaseCancelSpinLock(old_irql);
+ return;
+ }
+ else
+ {
+ IoSetCancelRoutine(irp, NULL);
+ IoReleaseCancelSpinLock(old_irql);
+ }
+
+ dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+
+ if( dev_ext && dev_ext->start_io )
+ {
+ dev_ext->start_io( dev_obj, irp );
+ return;
+ }
+
+ irp->IoStatus.Information = 0;
+ irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+
+ IoStartNextPacket( dev_obj, FALSE );
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+}
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ BOOLEAN fRes;
+
+#if DBG
+ // should be done before any debug output is done.
+ // read our debug verbosity level from the registry
+ //NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path
+ // L"DebugLevel", // REG_DWORD ValueName
+ // &gDebugLevel ); // Value receiver
+
+ // debug_level = DBGLVL_MAXIMUM;
+#endif
+
+ ehci_dbg_print_cond( DBGLVL_MINIMUM , DEBUG_UHCI, ("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer ));
+
+ // Remember our driver object, for when we create our child PDO
+ usb_driver_obj = DriverObject;
+
+ //
+ // Create dispatch points for create, close, unload
+ DriverObject->MajorFunction[ IRP_MJ_CREATE ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = generic_dispatch_irp;
+ DriverObject->DriverUnload = ehci_unload;
+
+ // User mode DeviceIoControl() calls will be routed here
+ DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = generic_dispatch_irp;
+
+ // User mode ReadFile()/WriteFile() calls will be routed here
+ DriverObject->MajorFunction[ IRP_MJ_WRITE ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_READ ] = generic_dispatch_irp;
+
+ DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_SCSI ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = generic_dispatch_irp;
+
+ DriverObject->DriverStartIo = generic_start_io;
+ // routines for handling system PNP and power management requests
+ //DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp;
+
+ // The Functional Device Object (FDO) will not be created for PNP devices until
+ // this routine is called upon device plug-in.
+ RtlZeroMemory( &g_dev_mgr, sizeof( USB_DEV_MANAGER ) );
+ g_dev_mgr.usb_driver_obj = DriverObject;
+
+ ehci_probe( DriverObject, RegistryPath, &g_dev_mgr );
+
+ if( dev_mgr_strobe( &g_dev_mgr ) == FALSE )
+ {
+ dev_mgr_release_hcd( &g_dev_mgr );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ dev_mgr_start_hcd( &g_dev_mgr );
+ ehci_dbg_print_cond( DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus));
+ return STATUS_SUCCESS;
+}
+#endif
+
+//note: the initialization will be in the following order
+// dev_mgr_strobe
+// ehci_start
+
+// to kill dev_mgr_thread:
+// dev_mgr->term_flag = TRUE;
+// KeSetEvent( &dev_mgr->wake_up_event );
+// this piece of code must run at passive-level
+//
+//
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.h b/reactos/drivers/usb/nt4compat/usbdriver/ehci.h
new file mode 100644
index 00000000000..4b4d38b764f
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.h
@@ -0,0 +1,810 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "hcd.h"
+#include "usb.h"
+#include "td.h"
+#ifndef __EHCI_H__
+#define __EHCI_H__
+
+#define EHCI_QH_SIZE sizeof( EHCI_QH )
+#define EHCI_ITD_SIZE sizeof( EHCI_ITD )
+#define EHCI_SITD_SIZE sizeof( EHCI_SITD )
+#define EHCI_FSTN_SIZE sizeof( EHCI_FSTN )
+#define EHCI_QTD_SIZE sizeof( EHCI_QTD )
+
+#define INIT_LIST_FLAG_TYPE 0x0f
+#define INIT_LIST_FLAG_ITD 0x00
+#define INIT_LIST_FLAG_QH 0x01
+#define INIT_LIST_FLAG_SITD 0x02
+#define INIT_LIST_FLAG_FSTN 0x03
+#define INIT_LIST_FLAG_QTD 0x04
+
+#define EHCI_MAX_SIZE_TRANSFER 0x100000 // 1 MB per transfer
+#define EHCI_MAX_ELEMS_POOL 1024
+#define EHCI_MAX_LISTS_POOL 0x08
+#define EHCI_MAX_QHS_LIST 256 // 4 pages
+#define EHCI_MAX_ITDS_LIST ( PAGE_SIZE / EHCI_ITD_SIZE * 2 ) // 2 pages
+#define EHCI_MAX_SITDS_LIST ( PAGE_SIZE / EHCI_SITD_SIZE )// 1 page
+#define EHCI_MAX_FSTNS_LIST ( PAGE_SIZE / EHCI_FSTN_SIZE )// 1 page
+#define EHCI_MAX_QTDS_LIST 256 // 4 pages
+
+#define EHCI_PTR_TERM 0x01
+#define EHCI_NAK_RL_COUNT 0x04
+
+#define EHCI_QTD_MAX_TRANS_SIZE 20480
+#define PHYS_PART_TYPE_MASK 0x01f
+#define PHYS_PART_ADDR_MASK ( ~PHYS_PART_TYPE_MASK )
+
+typedef struct _EHCI_ELEM_LINKS
+{
+ LIST_ENTRY elem_link; // link for one urb request
+ LIST_ENTRY sched_link; // to shadow link of schedule
+ struct _EHCI_ELEM_POOL* pool_link;
+ struct _EHCI_ELEM_LIST* list_link; // opaque to client, which list this td belongs to
+ PVOID phys_part; // point to EHCI_XXX, the lower 5 bits is used to indicate the type of the element
+ PURB purb;
+
+} EHCI_ELEM_LINKS, *PEHCI_ELEM_LINKS;
+
+typedef struct _INIT_ELEM_LIST_CONTEXT
+{
+ PADAPTER_OBJECT padapter;
+ struct _EHCI_ELEM_POOL *pool;
+
+} INIT_ELEM_LIST_CONTEXT, *PINIT_ELEM_LIST_CONTEXT;
+
+typedef VOID ( *PDESTROY_ELEM_LIST )( struct _EHCI_ELEM_LIST* plist );
+typedef PLIST_ENTRY ( *PGET_LIST_HEAD )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PGET_TOTAL_COUNT )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PGET_ELEM_SIZE )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PGET_LINK_OFFSET )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PADD_REF )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PRELEASE_REF )( struct _EHCI_ELEM_LIST* plist );
+typedef LONG ( *PGET_REF )( struct _EHCI_ELEM_LIST* plist );
+
+typedef struct _EHCI_ELEM_LIST
+{
+ PDESTROY_ELEM_LIST destroy_list;
+ PGET_LIST_HEAD get_list_head;
+ PGET_TOTAL_COUNT get_total_count;
+ PGET_ELEM_SIZE get_elem_size;
+ PGET_LINK_OFFSET get_link_offset;
+ PADD_REF add_ref;
+ PRELEASE_REF release_ref;
+ PGET_REF get_ref;
+
+ // private area
+ LONG flags; // contails element's type info
+ LONG total_count;
+ LONG elem_size;
+ LIST_HEAD free_list; // chains of all the elements
+ struct _EHCI_ELEM_POOL *parent_pool;
+ LONG reference;
+
+ // used to represent the physical memory
+ PPHYSICAL_ADDRESS phys_addrs; // array of non-contigous memory
+ PBYTE *phys_bufs;
+ PEHCI_ELEM_LINKS elem_head_buf;
+ PADAPTER_OBJECT padapter;
+
+} EHCI_ELEM_LIST, *PEHCI_ELEM_LIST;
+
+BOOL elem_pool_init_pool( struct _EHCI_ELEM_POOL* pool, LONG flags, PVOID context);
+VOID elem_pool_destroy_pool ( struct _EHCI_ELEM_POOL* pool );
+PEHCI_ELEM_LINKS elem_pool_alloc_elem ( struct _EHCI_ELEM_POOL* pool );
+VOID elem_pool_free_elem ( PEHCI_ELEM_LINKS elem_link );
+LONG elem_pool_get_total_count ( struct _EHCI_ELEM_POOL* pool );
+BOOL elem_pool_is_empty ( struct _EHCI_ELEM_POOL* pool );
+LONG elem_pool_get_free_count ( struct _EHCI_ELEM_POOL* pool );
+LONG elem_pool_get_link_offset ( struct _EHCI_ELEM_POOL* pool );
+PEHCI_ELEM_LINKS elem_pool_alloc_elems ( struct _EHCI_ELEM_POOL* pool, LONG count );
+BOOL elem_pool_free_elems( PEHCI_ELEM_LINKS elem_chains );
+LONG elem_pool_get_type( struct _EHCI_ELEM_POOL* pool );
+
+// the following are private functions
+BOOL elem_pool_expand_pool( struct _EHCI_ELEM_POOL* pool, LONG elem_count );
+BOOL elem_pool_collect_garbage( struct _EHCI_ELEM_POOL* pool );
+
+// lock operations
+BOOL elem_pool_lock( struct _EHCI_ELEM_POOL* pool, BOOL at_dpc );
+BOOL elem_pool_unlock( struct _EHCI_ELEM_POOL* pool, BOOL at_dpc );
+
+// helper
+LONG get_elem_phys_part_size( ULONG type );
+
+typedef struct _EHCI_ELEM_POOL
+{
+ LONG flags;
+ LONG free_count; // free count of all the lists currently allocated
+ LONG link_offset; // a cache for elem_list->get_link_offset
+ LONG list_count; // lists currently allocated
+ PEHCI_ELEM_LIST elem_lists[ EHCI_MAX_LISTS_POOL ];
+
+} EHCI_ELEM_POOL, *PEHCI_ELEM_POOL;
+
+/*-------------------------------------------------------------------------*/
+
+#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...)
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+#define QTD_TOGGLE (1 << 31) /* data toggle */
+#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define QTD_IOC (1 << 15) /* interrupt on complete */
+#define QTD_CERR(tok) (((tok)>>10) & 0x3)
+#define QTD_PID(tok) (((tok)>>8) & 0x3)
+#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
+#define QTD_STS_HALT (1 << 6) /* halted on error */
+#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
+#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
+#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
+#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
+#define QTD_STS_STS (1 << 1) /* split transaction state */
+#define QTD_STS_PING (1 << 0) /* issue PING? */
+#define QTD_ANY_ERROR ( QTD_STS_HALT | QTD_STS_DBE | QTD_STS_XACT | QTD_STS_MMF | QTD_STS_BABBLE )
+
+#define QTD_PID_SETUP 0x02
+#define QTD_PID_IN 0x01
+#define QTD_PID_OUT 0x00
+
+#pragma pack( push, usb_align, 1 )
+
+typedef struct _EHCI_QTD_CONTENT
+{
+ // DWORD 0
+ ULONG terminal : 1;
+ ULONG reserved : 4;
+ ULONG next_qtd : 27;
+
+ // DWORD 1
+ ULONG alt_terminal : 1;
+ ULONG reserved1 : 4;
+ ULONG alt_qtd : 27;
+
+ // DWORD 2
+ ULONG status : 8;
+ ULONG pid : 2;
+ ULONG err_count : 2;
+ ULONG cur_page : 3;
+ ULONG ioc : 1;
+ ULONG bytes_to_transfer : 15;
+ ULONG data_toggle : 1;
+
+ // DWORD 3
+ ULONG cur_offset : 12;
+ ULONG page0 : 20;
+
+ // DWORD 4-
+ ULONG pages[ 4 ];
+
+} EHCI_QTD_CONTENT, *PEHCI_QTD_CONTENT;
+
+typedef struct _EHCI_QTD
+{
+ /* first part defined by EHCI spec */
+ ULONG hw_next; /* see EHCI 3.5.1 */
+ ULONG hw_alt_next; /* see EHCI 3.5.2 */
+ ULONG hw_token; /* see EHCI 3.5.3 */
+ ULONG hw_buf [5]; /* see EHCI 3.5.4 */
+
+ /* the rest is HCD-private */
+ PEHCI_ELEM_LINKS elem_head_link;
+ ULONG phys_addr; /* qtd address */
+ USHORT bytes_to_transfer; /* the bytes_to_transfer in hw_token will drop to zero when completed*/
+ USHORT reserved2;
+ ULONG reserved[ 5 ];
+
+} EHCI_QTD, *PEHCI_QTD; //__attribute__ ((aligned (32)));
+
+#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,sitd,fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_ITD (0 << 1)
+#define Q_TYPE_QH (1 << 1)
+#define Q_TYPE_SITD (2 << 1)
+#define Q_TYPE_FSTN (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+#define QH_HEAD 0x00008000
+#define QH_STATE_LINKED 1 /* HC sees this */
+#define QH_STATE_UNLINK 2 /* HC may still see this */
+#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
+#define NO_FRAME ((unsigned short)~0) /* pick new start */
+
+typedef struct _EHCI_QH_CONTENT
+{
+ // DWORD 0
+ ULONG terminal : 1;
+ ULONG ptr_type : 2;
+ ULONG reserved : 2;
+ ULONG next_link : 27;
+
+ // DWORD 1
+ ULONG dev_addr : 7;
+ ULONG inactive : 1;
+ ULONG endp_addr : 4;
+ ULONG endp_spd : 2;
+ ULONG data_toggle : 1;
+ ULONG is_async_head : 1;
+ ULONG max_packet_size : 11;
+ ULONG is_ctrl_endp : 1;
+ ULONG reload_counter : 4;
+
+ // DWORD 2
+ ULONG s_mask : 8;
+ ULONG c_mask : 8;
+ ULONG hub_addr : 7;
+ ULONG port_idx : 7;
+ ULONG mult : 2;
+
+ // DWORD 3
+ ULONG cur_qtd_ptr;
+
+ // overlay
+ EHCI_QTD_CONTENT cur_qtd;
+
+} EHCI_QH_CONTENT, *PEHCI_QH_CONTENT;
+
+typedef struct _EHCI_QH
+{
+ /* first part defined by EHCI spec */
+ ULONG hw_next; /* see EHCI 3.6.1 */
+ ULONG hw_info1; /* see EHCI 3.6.2 */
+ ULONG hw_info2; /* see EHCI 3.6.2 */
+ ULONG hw_current; /* qtd list - see EHCI 3.6.4 */
+
+ /* qtd overlay (hardware parts of a struct ehci_qtd) */
+ ULONG hw_qtd_next;
+ ULONG hw_alt_next;
+ ULONG hw_token;
+ ULONG hw_buf [5];
+
+ /* the rest is HCD-private */
+ PEHCI_ELEM_LINKS elem_head_link;
+ ULONG phys_addr; /* address of qh */
+ ULONG reserved[ 2 ];
+
+} EHCI_QH, *PEHCI_QH; // __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+
+#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
+#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
+#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
+#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff)
+#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
+
+#define ITD_STS_ACTIVE 8
+#define ITD_STS_BUFERR 4
+#define ITD_STS_BABBLE 2
+#define ITD_STS_XACTERR 1
+
+#define ITD_ANY_ERROR ( ITD_STS_BUFERR | ITD_STS_BABBLE | ITD_STS_XACTERR )
+
+typedef struct _EHCI_ITD_STATUS_SLOT
+{
+ ULONG offset : 12;
+ ULONG page_sel : 3;
+ ULONG ioc : 1;
+ ULONG trans_length : 12;
+ ULONG status : 4;
+
+} EHCI_ITD_STATUS_SLOT, *PEHCI_ITD_STATUS_SLOT;
+
+typedef struct _EHCI_ITD_CONTENT
+{
+ // DWORD 0;
+ ULONG terminal : 1;
+ ULONG ptr_type : 2;
+ ULONG reserved : 2;
+ ULONG next_link : 27;
+ // DWORD 1-8;
+ EHCI_ITD_STATUS_SLOT status_slot[ 8 ];
+ // DWORD 9;
+ ULONG dev_addr : 7;
+ ULONG reserved1 : 1;
+ ULONG endp_num : 4;
+ ULONG page0 : 20;
+ // DWORD 10;
+ ULONG max_packet_size : 11;
+ ULONG io_dir : 1;
+ ULONG page1 : 20;
+ // DWORD 11;
+ ULONG mult : 2;
+ ULONG reserved2 : 10;
+ ULONG page2 : 20;
+ // DWORD 12-;
+ ULONG pages[ 4 ];
+
+} EHCI_ITD_CONTENT, *PEHCI_ITD_CONTENT;
+
+typedef struct _EHCI_ITD {
+
+ /* first part defined by EHCI spec */
+ ULONG hw_next; /* see EHCI 3.3.1 */
+ ULONG hw_transaction [8]; /* see EHCI 3.3.2 */
+ ULONG hw_bufp [7]; /* see EHCI 3.3.3 */
+
+ /* the rest is EHCI-private */
+ PEHCI_ELEM_LINKS elem_head_link;
+ ULONG phys_addr; /* for this itd */
+ ULONG buf_phys_addr; /* buffer address */
+ ULONG reserved[ 5 ];
+
+} EHCI_ITD, *PEHCI_ITD; // __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.4
+ * siTD, aka split-transaction isochronous Transfer Descriptor
+ * ... describe low/full speed iso xfers through TT in hubs
+ * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
+ */
+#define SITD_STS_ACTIVE 0x80
+#define SITD_STS_ERR 0x40 // error from device
+#define SITD_STS_DBE 0x20 // data buffer error
+#define SITD_STS_BABBLE 0x10 // data more than expected
+#define SITD_STS_XACTERR 0x08 // general error on hc
+#define SITD_STS_MISSFRM 0x04 // missd micro frames
+#define SITD_STS_SC 0x02 // state is whether start-split( 0 ) or complete-split( 1 )
+#define SITD_STS_RESERVED 0x01
+#define SITD_ANY_ERROR ( SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE | SITD_STS_XACTERR | SITD_STS_MISSFRM )
+
+typedef struct _EHCI_SITD_CONTENT
+{
+ // DWORD 0;
+ ULONG terminal : 1;
+ ULONG ptr_type : 2;
+ ULONG reserved : 2;
+ ULONG next_link : 27;
+ // DWORD 1;
+ ULONG dev_addr : 7;
+ ULONG reserved1 : 1;
+ ULONG endp_num : 4;
+ ULONG reserved2 : 4;
+ ULONG hub_addr : 7;
+ ULONG reserved3 : 1;
+ ULONG port_idx : 7;
+ ULONG io_dir : 1;
+ // DWORD 2;
+ ULONG s_mask : 8;
+ ULONG c_mask : 8;
+ ULONG reserved4 : 16;
+ // DWORD 3:
+ ULONG status : 8;
+ ULONG c_prog_mask : 8;
+ ULONG bytes_to_transfer : 10;
+ ULONG reserved5 : 4;
+ ULONG page_sel : 1;
+ ULONG ioc : 1;
+ // DWORD 4;
+ ULONG cur_offset : 12;
+ ULONG page0 : 20;
+ // DWORD 5;
+ ULONG trans_count : 3;
+ ULONG trans_pos : 2;
+ ULONG reserved6 : 7;
+ ULONG page1 : 20;
+ // DWORD 6;
+ ULONG back_terminal : 1;
+ ULONG reserved7 : 4;
+ ULONG back_ptr : 27;
+
+} EHCI_SITD_CONTENT, *PEHCI_SITD_CONTENT;
+
+typedef struct _EHCI_SITD
+{
+ /* first part defined by EHCI spec */
+ ULONG hw_next;
+ /* uses bit field macros above - see EHCI 0.95 Table 3-8 */
+
+ ULONG hw_fullspeed_ep; /* see EHCI table 3-9 */
+ ULONG hw_uframe; /* see EHCI table 3-10 */
+ ULONG hw_tx_results1; /* see EHCI table 3-11 */
+ ULONG hw_tx_results2; /* see EHCI table 3-12 */
+ ULONG hw_tx_results3; /* see EHCI table 3-12 */
+ ULONG hw_backpointer; /* see EHCI table 3-13 */
+
+ /* the rest is HCD-private */
+ PEHCI_ELEM_LINKS elem_head_link;
+ ULONG phys_addr;
+ ULONG buf_phys_addr; /* buffer address */
+
+ USHORT usecs; /* start bandwidth */
+ USHORT c_usecs; /* completion bandwidth */
+ ULONG reserved[ 5 ];
+
+} EHCI_SITD, *PEHCI_SITD; // __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+typedef struct _EHCI_FSTN
+{
+ ULONG hw_next; /* any periodic q entry */
+ ULONG hw_prev; /* qh or EHCI_LIST_END */
+
+ /* the rest is HCD-private */
+ PEHCI_ELEM_LINKS elem_head_link;
+ ULONG phys_addr;
+ ULONG reserved[ 4 ];
+
+} EHCI_FSTN, *PEHCI_FSTN; // __attribute__ ((aligned (32)));
+
+
+/* NOTE: urb->transfer_flags expected to not use this bit !!! */
+#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+/* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_PR (1<<8) /* reset port */
+#define PORT_SUSP (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CCS (1<<0) /* device connected */
+
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+typedef struct _EHCI_HCS_CONTENT
+{
+ ULONG port_count : 4;
+ ULONG port_power_control : 1;
+ ULONG reserved : 2;
+ ULONG port_rout_rules : 1;
+ ULONG port_per_chc : 4;
+ ULONG chc_count : 4;
+ ULONG port_indicator : 1;
+ ULONG reserved2 : 3;
+ ULONG dbg_port_num : 4;
+ ULONG reserved3 : 8;
+
+} EHCI_HCS_CONTENT, *PEHCI_HCS_CONTENT;
+
+typedef struct _EHCI_HCC_CONTENT
+{
+ ULONG cur_addr_bits : 1; /* 0: 32 bit addressing 1: 64 bit addressing */
+ ULONG var_frame_list : 1; /* 0: 1024 frames, 1: support other number of frames */
+ ULONG park_mode : 1;
+ ULONG reserved : 1;
+ ULONG iso_sched_threshold : 4;
+ ULONG eecp_capable : 8;
+ ULONG reserved2 : 16;
+
+} EHCI_HCC_CONTENT, *PEHCI_HCC_CONTENT;
+
+typedef struct _EHCI_CAPS {
+ UCHAR length; /* CAPLENGTH - size of this struct */
+ UCHAR reserved; /* offset 0x1 */
+ USHORT hci_version; /* HCIVERSION - offset 0x2 */
+ ULONG hcs_params; /* HCSPARAMS - offset 0x4 */
+
+ ULONG hcc_params; /* HCCPARAMS - offset 0x8 */
+ UCHAR portroute [8]; /* nibbles for routing - offset 0xC */
+
+} EHCI_CAPS, *PEHCI_CAPS;
+
+/* Section 2.3 Host Controller Operational Registers */
+
+#define EHCI_USBCMD 0x00
+#define EHCI_USBSTS 0x04
+#define EHCI_USBINTR 0x08
+#define EHCI_FRINDEX 0x0c
+#define EHCI_CTRLDSSEGMENT 0x10
+#define EHCI_PERIODICLISTBASE 0x14
+#define EHCI_ASYNCLISTBASE 0x18
+#define EHCI_CONFIGFLAG 0x40
+#define EHCI_PORTSC 0x44
+
+#define EHCI_USBINTR_INTE 0x01
+#define EHCI_USBINTR_ERR 0x02
+#define EHCI_USBINTR_PC 0x04
+#define EHCI_USBINTR_FLROVR 0x08
+#define EHCI_USBINTR_HSERR 0x10
+#define EHCI_USBINTR_ASYNC 0x20
+
+typedef struct _EHCI_USBCMD_CONTENT
+{
+ ULONG run_stop : 1;
+ ULONG hcreset : 1;
+ ULONG frame_list_size : 2;
+ ULONG periodic_enable : 1;
+ ULONG async_enable : 1;
+ ULONG door_bell : 1;
+ ULONG light_reset : 1;
+ ULONG async_park_count : 2;
+ ULONG reserved : 1;
+ ULONG async_park_enable : 1;
+ ULONG reserved1 : 4;
+ ULONG int_threshold : 8;
+ ULONG reserved2 : 8;
+
+} EHCI_USBCMD_CONTENT, *PEHCI_USBCMD_CONTENT;
+
+typedef struct _EHCI_USBSTS_CONTENT
+{
+ ULONG ioc : 1;
+ ULONG trasac_error : 1;
+ ULONG port_change : 1;
+ ULONG fl_rollover : 1;
+ ULONG host_system_error : 1;
+ ULONG async_advance : 1;
+ ULONG reserved : 6;
+ ULONG hc_halted : 1;
+ ULONG reclaimation : 1;
+ ULONG periodic_status : 1;
+ ULONG async_status : 1;
+ ULONG reserved1 : 16;
+
+} EHCI_USBSTS_CONTENT, *PEHCI_USBSTS_CONTENT;
+
+typedef struct _EHCI_RHPORTSC_CONTENT
+{
+ ULONG cur_connect : 1;
+ ULONG cur_connect_change : 1;
+ ULONG port_enable : 1;
+ ULONG port_enable_change : 1;
+ ULONG over_current : 1;
+ ULONG over_current_change : 1;
+ ULONG force_port_resume : 1;
+ ULONG suspend : 1;
+ ULONG port_reset : 1;
+ ULONG reserved : 1;
+ ULONG line_status : 2;
+ ULONG port_power : 1;
+ ULONG port_owner : 1;
+ ULONG port_indicator : 2;
+ ULONG port_test : 4;
+ ULONG we_connect : 1;
+ ULONG we_disconnect : 1;
+ ULONG we_over_current : 1;
+ ULONG reserved1 : 9;
+
+} EHCI_RHPORTSC_CONTENT, *PEHCI_RHPORTSC_CONTENT;
+
+typedef struct _EHCI_REGS {
+
+ ULONG command;
+ ULONG status;
+ ULONG intr_enable;
+ ULONG frame_index; /* current microframe number */
+ ULONG segment; /* address bits 63:32 if needed */
+ ULONG frame_list; /* points to periodic list */
+ ULONG async_next; /* address of next async queue head */
+ ULONG reserved [9];
+ ULONG configured_flag;
+ ULONG port_status [0]; /* up to N_PORTS */
+
+} EHCI_REGS, *PEHCI_REGS;
+
+#pragma pack( pop, usb_align )
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, reclaim, periodic (and shadow), ...
+ * hcd_dev: ep[]
+ * ehci_qh: qh_next, qtd_list
+ * ehci_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+
+#define EHCI_DEVICE_NAME "\\Device\\EHCI"
+
+#define EHCI_DOS_DEVICE_NAME "\\DosDevices\\EHCI"
+
+#define EHCI_ITD_POOL_IDX INIT_LIST_FLAG_ITD
+#define EHCI_QH_POOL_IDX INIT_LIST_FLAG_QH
+#define EHCI_SITD_POOL_IDX INIT_LIST_FLAG_SITD
+#define EHCI_FSTN_POOL_IDX INIT_LIST_FLAG_FSTN
+#define EHCI_QTD_POOL_IDX INIT_LIST_FLAG_QTD
+
+#define EHCI_DEFAULT_FRAMES UHCI_MAX_FRAMES
+#define EHCI_MAX_SYNC_BUS_TIME 50000 // stands for 100000 ns, only to get wrapped within one word
+
+#define EHCI_SCHED_INT8_INDEX 0
+#define EHCI_SCHED_INT4_INDEX 1
+#define EHCI_SCHED_INT2_INDEX 2
+#define EHCI_SCHED_FSTN_INDEX 3
+#define EHCI_SCHED_INT1_INDEX 4
+
+#define qtd_pool ( &ehci->elem_pools[ EHCI_QTD_POOL_IDX ] )
+#define qh_pool ( &ehci->elem_pools[ EHCI_QH_POOL_IDX ] )
+#define fstn_pool ( &ehci->elem_pools[ EHCI_FSTN_POOL_IDX ] )
+#define itd_pool ( &ehci->elem_pools[ EHCI_ITD_POOL_IDX ] )
+#define sitd_pool ( &ehci->elem_pools[ EHCI_SITD_POOL_IDX ] )
+
+
+typedef struct _EHCI_DEV
+{
+ HCD hcd_interf;
+
+ EHCI_CAPS ehci_caps;
+
+ PHYSICAL_ADDRESS ehci_reg_base; // io space
+ BOOL port_mapped;
+ PBYTE port_base; // note: added by ehci_caps.length, operational regs base addr, not the actural base
+
+ ULONG frame_count;
+ PHYSICAL_ADDRESS frame_list_phys_addr;
+
+ KSPIN_LOCK frame_list_lock; // run at DIRQL
+ PULONG frame_list; // periodic schedule
+
+ PFRAME_LIST_CPU_ENTRY frame_list_cpu; // periodic schedule shadow
+
+ LIST_HEAD urb_list; // active urb-list
+
+ LIST_HEAD async_list_cpu;
+ LIST_HEAD periodic_list_cpu[ 8 ]; // each slot for one periodic
+ PEHCI_QH skel_async_qh;
+
+
+ //
+ // pools for device specific data
+ //
+ EHCI_ELEM_POOL elem_pools[ 5 ];
+
+ //
+ //for iso and int bandwidth claim, bandwidth schedule
+ //
+ KSPIN_LOCK pending_endp_list_lock; //lock to access the following two
+ LIST_HEAD pending_endp_list;
+ UHCI_PENDING_ENDP_POOL pending_endp_pool;
+ PUSHORT frame_bw; //unit uFrame
+ USHORT min_bw; //the bottle-neck of the bandwidths across frame-list
+
+ KTIMER reset_timer; //used to reset the host controller
+ struct _EHCI_DEVICE_EXTENSION *pdev_ext;
+ PUSB_DEV root_hub; //root hub
+
+} EHCI_DEV, *PEHCI_DEV;
+
+typedef UHCI_PORT EHCI_MEMORY;
+
+typedef struct _EHCI_DEVICE_EXTENSION
+{
+ //struct _USB_DEV_MANAGER *pdev_mgr;
+ DEVEXT_HEADER dev_ext_hdr;
+ PDEVICE_OBJECT pdev_obj;
+ PDRIVER_OBJECT pdrvr_obj;
+ PEHCI_DEV ehci;
+
+ //device resources
+ PADAPTER_OBJECT padapter;
+ ULONG map_regs;
+ PCM_RESOURCE_LIST res_list;
+ ULONG pci_addr; // bus number | slot number | funciton number
+ UHCI_INTERRUPT res_interrupt;
+ union
+ {
+ UHCI_PORT res_port;
+ EHCI_MEMORY res_memory;
+ };
+
+ PKINTERRUPT ehci_int;
+ KDPC ehci_dpc;
+
+} EHCI_DEVICE_EXTENSION, *PEHCI_DEVICE_EXTENSION;
+
+/*-------------------------------------------------------------------------*/
+
+#endif /* __EHCI_H__ */
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc b/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc
new file mode 100644
index 00000000000..d5153dcf41d
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/ehci.rc
@@ -0,0 +1,45 @@
+// Resource script for USBISO driver
+// Generated by Walt Oney's driver wizard
+
+#include
+
+LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,1,0
+ PRODUCTVERSION 0,0,1,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "This is a beta version of usb driver stack( ehci ), contact me at mypublic99@yahoo.com\0"
+ VALUE "CompanyName", "Woodhead Software\0"
+ VALUE "FileDescription", "ehci.sys\0"
+ VALUE "FileVersion", "0, 0, 1, 0\0"
+ VALUE "InternalName", "ehci.sys\0"
+ VALUE "LegalCopyright", "Copyright © 2002-2004 Woodhead Software\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "ehci.sys\0"
+ VALUE "PrivateBuild", "0.01\0"
+ VALUE "ProductName", "usb driver stack for windows NT\0"
+ VALUE "ProductVersion", "0, 0, 1, 0\0"
+ VALUE "SpecialBuild", "0131.d\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/etd.c b/reactos/drivers/usb/nt4compat/usbdriver/etd.c
new file mode 100644
index 00000000000..c15e34d777c
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/etd.c
@@ -0,0 +1,733 @@
+/**
+ * etd.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ehci.h"
+#include "debug.h"
+
+#define init_elem( ptr, type, ehci_elem_type ) \
+{\
+ PEHCI_ELEM_LINKS tmp;\
+ ptr = ( type* )( plist->phys_bufs[ i ] + sizeof( type ) * j );\
+ ptr->hw_next = ehci_elem_type << 1;\
+ if( ehci_elem_type == INIT_LIST_FLAG_QTD )\
+ ptr->hw_next = 0;\
+ tmp = ptr->elem_head_link = &plist->elem_head_buf[ i * elms_per_page + j ];\
+ InitializeListHead( &tmp->elem_link );\
+ InitializeListHead( &tmp->sched_link );\
+ InsertTailList( &plist->free_list, &tmp->elem_link );\
+ tmp->list_link = plist;\
+ tmp->pool_link = pinit_ctx->pool;\
+ tmp->phys_part = ( PVOID )( ( ULONG )ptr | ( ehci_elem_type << 1 ) );\
+ if( ehci_elem_type != INIT_LIST_FLAG_QTD )\
+ ptr->phys_addr = ( plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ) | ( ehci_elem_type << 1 );\
+ else \
+ ptr->phys_addr = plist->phys_addrs[ i ].LowPart + sizeof( type ) * j ;\
+}
+
+// get the actual max list count of the pool, two limit, max_elem_pool and max_lists_pool
+#define get_max_lists_count( pOOL, max_liSTS ) \
+{\
+ LONG ii1;\
+ switch( elem_pool_get_type( pOOL ) )\
+ {\
+ case INIT_LIST_FLAG_QTD:\
+ ii1 = EHCI_MAX_QTDS_LIST;\
+ break;\
+ case INIT_LIST_FLAG_FSTN:\
+ ii1 = EHCI_MAX_QHS_LIST;\
+ break;\
+ case INIT_LIST_FLAG_SITD:\
+ ii1 = EHCI_MAX_ITDS_LIST;\
+ break;\
+ case INIT_LIST_FLAG_QH: \
+ ii1 = EHCI_MAX_SITDS_LIST;\
+ break;\
+ case INIT_LIST_FLAG_ITD: \
+ ii1 = EHCI_MAX_FSTNS_LIST;\
+ break;\
+ }\
+ max_liSTS = ( EHCI_MAX_ELEMS_POOL / ii1 ) > EHCI_MAX_LISTS_POOL ? EHCI_MAX_LISTS_POOL : ( EHCI_MAX_ELEMS_POOL / ii1 );\
+}
+
+VOID
+elem_list_destroy_elem_list(
+PEHCI_ELEM_LIST plist
+);
+
+PLIST_ENTRY
+elem_list_get_list_head(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_get_total_count(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_get_elem_size(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_get_link_offset(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_add_ref(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_release_ref(
+PEHCI_ELEM_LIST plist
+);
+
+LONG
+elem_list_get_ref(
+PEHCI_ELEM_LIST plist
+);
+
+BOOL
+elem_pool_lock(
+PEHCI_ELEM_POOL pool,
+BOOL at_dpc
+)
+{
+ return TRUE;
+}
+BOOL
+elem_pool_unlock(
+PEHCI_ELEM_POOL pool,
+BOOL at_dpc
+)
+{
+ return TRUE;
+}
+LONG
+get_elem_phys_part_size(
+ULONG type
+)
+{
+ // type is INIT_LIST_FLAG_XXX
+ LONG size;
+
+ size = 0;
+ switch( type )
+ {
+ case INIT_LIST_FLAG_ITD:
+ size = 64;
+ break;
+ case INIT_LIST_FLAG_SITD:
+ size = 28;
+ break;
+ case INIT_LIST_FLAG_QTD:
+ size = 32;
+ break;
+ case INIT_LIST_FLAG_QH:
+ size = 48;
+ break;
+ case INIT_LIST_FLAG_FSTN:
+ size = 8;
+ break;
+ }
+ return size;
+}
+
+BOOL
+elem_list_init_elem_list(
+PEHCI_ELEM_LIST plist,
+LONG init_flags,
+PVOID context,
+LONG count
+)
+{
+ LONG pages, i, j, elms_per_page;
+ PEHCI_QH pqh;
+ PEHCI_ITD pitd;
+ PEHCI_SITD psitd;
+ PEHCI_QTD pqtd;
+ PEHCI_FSTN pfstn;
+ PINIT_ELEM_LIST_CONTEXT pinit_ctx;
+
+ if( plist == NULL || context == NULL )
+ return FALSE;
+
+ RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) );
+
+ pinit_ctx = context;
+
+ plist->destroy_list = elem_list_destroy_elem_list;
+ plist->get_list_head = elem_list_get_list_head;
+ plist->get_total_count = elem_list_get_total_count;
+ plist->get_elem_size = elem_list_get_elem_size;
+ plist->get_link_offset = elem_list_get_link_offset;
+ plist->add_ref = elem_list_add_ref;
+ plist->release_ref = elem_list_release_ref;
+ plist->get_ref = elem_list_get_ref;
+
+ InitializeListHead( &plist->free_list );
+
+ switch( init_flags & 0x0f )
+ {
+ case INIT_LIST_FLAG_ITD:
+ plist->total_count = EHCI_MAX_ITDS_LIST;
+ plist->elem_size = sizeof( EHCI_ITD );
+ break;
+ case INIT_LIST_FLAG_QH:
+ plist->total_count = EHCI_MAX_QHS_LIST;
+ plist->elem_size = sizeof( EHCI_QH );
+ break;
+ case INIT_LIST_FLAG_SITD:
+ plist->total_count = EHCI_MAX_SITDS_LIST;
+ plist->elem_size = sizeof( EHCI_SITD );
+ break;
+ case INIT_LIST_FLAG_FSTN:
+ plist->total_count = EHCI_MAX_FSTNS_LIST;
+ plist->elem_size = sizeof( EHCI_FSTN );
+ break;
+ case INIT_LIST_FLAG_QTD:
+ plist->total_count = EHCI_MAX_QTDS_LIST;
+ plist->elem_size = sizeof( EHCI_QTD );
+ break;
+ default:
+ goto ERROR_OUT;
+ }
+ if( plist->elem_size & 0x1f )
+ {
+ plist->total_count = 0;
+ goto ERROR_OUT;
+ }
+
+ plist->flags = init_flags;
+ plist->parent_pool = pinit_ctx->pool;
+ plist->padapter = pinit_ctx->padapter;
+ pages = ( ( plist->elem_size * plist->total_count ) + ( PAGE_SIZE - 1 ) ) / PAGE_SIZE;
+ elms_per_page = PAGE_SIZE / plist->elem_size;
+
+ plist->phys_addrs = usb_alloc_mem( NonPagedPool,
+ ( sizeof( PHYSICAL_ADDRESS ) + sizeof( PBYTE ) ) * pages + \
+ sizeof( EHCI_ELEM_LINKS ) * plist->total_count );
+
+ if( plist->phys_addrs == NULL )
+ {
+ plist->total_count = 0;
+ goto ERROR_OUT;
+ }
+
+ plist->phys_bufs = ( PBYTE* )&plist->phys_addrs[ pages ];
+ plist->elem_head_buf = ( PEHCI_ELEM_LINKS )&plist->phys_bufs[ pages ];
+ RtlZeroMemory( plist->phys_addrs,
+ ( sizeof( PHYSICAL_ADDRESS ) + sizeof( PBYTE ) ) * pages + \
+ sizeof( EHCI_ELEM_LINKS ) * plist->total_count );
+
+ for( i = 0; i < pages; i++ )
+ {
+ plist->phys_bufs[ i ] = HalAllocateCommonBuffer(
+ plist->padapter,
+ PAGE_SIZE,
+ &plist->phys_addrs[ i ],
+ FALSE);
+
+ if( plist->phys_bufs[ i ] == NULL )
+ {
+ // failed, roll back
+ for( j = i - 1; j >= 0; j -- )
+ HalFreeCommonBuffer(
+ plist->padapter,
+ PAGE_SIZE,
+ plist->phys_addrs[ j ],
+ plist->phys_bufs[ j ],
+ FALSE );
+ goto ERROR_OUT;
+ }
+ RtlZeroMemory( plist->phys_bufs[ i ], PAGE_SIZE );
+ for( j = 0; j < elms_per_page; j++ )
+ {
+ switch( init_flags & 0xf )
+ {
+ case INIT_LIST_FLAG_QH:
+ {
+ init_elem( pqh, EHCI_QH, INIT_LIST_FLAG_QH );
+ break;
+ }
+ case INIT_LIST_FLAG_ITD:
+ {
+ init_elem( pitd, EHCI_ITD, INIT_LIST_FLAG_ITD );
+ break;
+ }
+ case INIT_LIST_FLAG_QTD:
+ {
+ init_elem( pqtd, EHCI_QTD, INIT_LIST_FLAG_QTD );
+ break;
+ }
+ case INIT_LIST_FLAG_SITD:
+ {
+ init_elem( psitd, EHCI_SITD, INIT_LIST_FLAG_SITD );
+ break;
+ }
+ case INIT_LIST_FLAG_FSTN:
+ {
+ init_elem( pfstn, EHCI_FSTN, INIT_LIST_FLAG_FSTN );
+ break;
+ }
+ default:
+ TRAP();
+ }
+ }
+ }
+ return TRUE;
+ERROR_OUT:
+
+ if( plist->phys_addrs != NULL )
+ usb_free_mem( plist->phys_addrs );
+
+ RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) );
+ return FALSE;
+}
+
+VOID
+elem_list_destroy_elem_list(
+PEHCI_ELEM_LIST plist
+)
+{
+ LONG i, pages;
+
+ if( plist == NULL )
+ return;
+
+ pages = ( plist->total_count * plist->elem_size + PAGE_SIZE - 1 ) / PAGE_SIZE;
+ for( i = 0; i < pages; i++ )
+ HalFreeCommonBuffer(
+ plist->padapter,
+ PAGE_SIZE,
+ plist->phys_addrs[ i ],
+ plist->phys_bufs[ i ],
+ FALSE );
+
+ usb_free_mem( plist->phys_addrs );
+ RtlZeroMemory( plist, sizeof( EHCI_ELEM_LIST ) );
+}
+
+PLIST_ENTRY
+elem_list_get_list_head(
+PEHCI_ELEM_LIST plist
+)
+{
+ if( plist == NULL )
+ return NULL;
+ return &plist->free_list;
+}
+
+LONG
+elem_list_get_total_count(
+PEHCI_ELEM_LIST plist
+)
+{
+ if( plist == NULL )
+ return 0;
+ return plist->total_count;;
+}
+
+LONG
+elem_list_get_elem_size(
+PEHCI_ELEM_LIST plist
+)
+{
+ if( plist == NULL )
+ return 0;
+ return plist->elem_size;
+}
+
+LONG
+elem_list_get_link_offset(
+PEHCI_ELEM_LIST plist
+)
+{
+ if( plist == NULL )
+ return 0;
+
+ return get_elem_phys_part_size( plist->flags & 0xf );
+}
+
+LONG
+elem_list_add_ref(
+PEHCI_ELEM_LIST plist
+)
+{
+ plist->reference++;
+ return plist->reference;
+}
+
+LONG
+elem_list_release_ref(
+PEHCI_ELEM_LIST plist
+)
+{
+ plist->reference--;
+ return plist->reference;
+}
+
+LONG
+elem_list_get_ref(
+PEHCI_ELEM_LIST plist
+)
+{
+ return plist->reference;
+}
+
+//
+// pool methods
+//
+
+BOOL
+elem_pool_init_pool(
+PEHCI_ELEM_POOL pool,
+LONG flags,
+PVOID context
+)
+{
+ PADAPTER_OBJECT padapter;
+ INIT_ELEM_LIST_CONTEXT init_ctx;
+
+ if( pool == NULL || context == NULL )
+ return FALSE;
+
+ RtlZeroMemory( pool, sizeof( EHCI_ELEM_POOL ) );
+
+ init_ctx.pool = pool;
+ init_ctx.padapter = context;
+
+ pool->elem_lists[ 0 ] = usb_alloc_mem( NonPagedPool, sizeof( EHCI_ELEM_LIST ) );
+
+ if( pool->elem_lists[ 0 ] == NULL )
+ return FALSE;
+
+ if( elem_list_init_elem_list( pool->elem_lists[ 0 ], flags, &init_ctx, 0 ) == FALSE )
+ {
+ usb_free_mem( pool->elem_lists[ 0 ] );
+ return FALSE;
+ }
+ pool->link_offset = pool->elem_lists[ 0 ]->get_link_offset( pool->elem_lists[ 0 ] );
+ pool->free_count = pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] );
+ pool->list_count = 1;
+ pool->flags = flags;
+
+ return TRUE;
+}
+
+LONG
+elem_pool_get_link_offset(
+PEHCI_ELEM_POOL elem_pool
+)
+{
+ return elem_pool->link_offset;
+}
+
+LONG
+elem_pool_get_total_count(
+PEHCI_ELEM_POOL elem_pool
+)
+{
+ return elem_pool->elem_lists[ 0 ]->get_total_count( elem_pool->elem_lists[ 0 ] ) * elem_pool->list_count;
+}
+
+VOID
+elem_pool_destroy_pool(
+PEHCI_ELEM_POOL pool
+)
+{
+ LONG i;
+ if( pool == NULL )
+ return;
+ for( i = pool->list_count - 1; i >= 0; i-- )
+ {
+ pool->elem_lists[ i ]->destroy_list( pool->elem_lists[ i ] );
+ usb_free_mem( pool->elem_lists[ i ] );
+ pool->elem_lists[ i ] = NULL;
+ }
+ RtlZeroMemory( pool, sizeof( EHCI_ELEM_POOL ) );
+ return;
+}
+
+PEHCI_ELEM_LINKS
+elem_pool_alloc_elem(
+PEHCI_ELEM_POOL pool
+)
+{
+ LONG i;
+ PEHCI_ELEM_LIST pel;
+ PLIST_HEAD lh;
+ PEHCI_ELEM_LINKS elnk;
+
+ if( pool == NULL )
+ return NULL;
+
+ for( i = 0; i < pool->list_count; i++ )
+ {
+ pel = pool->elem_lists[ i ];
+ if( pel->get_ref( pel ) == pel->get_total_count( pel ) )
+ continue;
+ break;
+ }
+ if( i == pool->list_count )
+ {
+ if( elem_pool_expand_pool( pool, pel->get_total_count( pel ) ) == FALSE )
+ return NULL;
+ pel = pool->elem_lists[ i ];
+ }
+
+ lh = pel->get_list_head( pel );
+ elnk = ( PEHCI_ELEM_LINKS )RemoveHeadList( lh );
+ InitializeListHead( &elnk->elem_link );
+ InitializeListHead( &elnk->sched_link );
+
+ pel->add_ref( pel );
+ pool->free_count--;
+
+ return elnk;
+}
+
+VOID
+elem_pool_free_elem(
+PEHCI_ELEM_LINKS elem_link
+)
+{
+ PLIST_HEAD lh;
+ LONG ref;
+ PEHCI_ELEM_POOL pool;
+ if( elem_link == NULL )
+ return;
+ pool = elem_link->pool_link;
+ lh = elem_link->list_link->get_list_head( elem_link->list_link );
+ if( lh == NULL )
+ return;
+ InsertHeadList( lh, ( PLIST_ENTRY )elem_link );
+ ref = elem_link->list_link->release_ref( elem_link->list_link );
+ pool->free_count++;
+ if( ref == 0 )
+ elem_pool_collect_garbage( pool );
+ return;
+}
+
+BOOL
+elem_pool_is_empty(
+PEHCI_ELEM_POOL pool
+)
+{
+ PEHCI_ELEM_LIST pel;
+
+ if( pool == NULL )
+ return TRUE;
+ pel = pool->elem_lists[ 0 ];
+ return ( pool->list_count == 1 && pool->free_count == pel->get_total_count( pel ) );
+}
+
+LONG
+elem_pool_get_free_count(
+PEHCI_ELEM_POOL pool
+)
+{
+ if( pool == NULL )
+ return 0;
+ return pool->free_count;
+}
+
+PEHCI_ELEM_LINKS
+elem_pool_alloc_elems(
+PEHCI_ELEM_POOL pool,
+LONG count
+)
+{
+ LIST_HEAD lh;
+ PLIST_ENTRY pthis;
+ LONG i, alloc_count, max_pool_lists;
+ PEHCI_ELEM_LIST pel;
+ PEHCI_ELEM_LINKS elnk;
+ // calculate to see if the count is affordable
+
+ if( pool == NULL || count <= 0 )
+ return NULL;
+
+ get_max_lists_count( pool, max_pool_lists );
+ InitializeListHead( &lh );
+ pel = pool->elem_lists[ 0 ];
+ if( count <= pool->free_count )
+ alloc_count = 0;
+ else
+ alloc_count = count - pool->free_count;
+
+ if( alloc_count > pel->get_total_count( pel ) * ( max_pool_lists - pool->list_count ) )
+ return NULL;
+
+ for( i = 0; i < count; i++ )
+ {
+ if( ( elnk = elem_pool_alloc_elem( pool ) ) == NULL )
+ {
+ // undo what we have done
+ while( IsListEmpty( &lh ) == FALSE )
+ {
+ pthis = RemoveHeadList( &lh );
+ elnk = struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link );
+ elem_pool_free_elem( elnk );
+ }
+ return NULL;
+ }
+ InsertTailList( &lh, &elnk->elem_link );
+ }
+ ListFirst( &lh, pthis );
+ elnk = struct_ptr( pthis, EHCI_ELEM_LINKS, elem_link );
+ RemoveEntryList( &lh );
+ return elnk;
+}
+
+BOOL
+elem_pool_free_elems(
+PEHCI_ELEM_LINKS elem_chains
+)
+{
+ // note: no list head exists.
+ LIST_HEAD lh;
+ PEHCI_ELEM_LINKS elnk;
+
+ InsertTailList( &elem_chains->elem_link, &lh );
+ while( IsListEmpty( &lh ) == FALSE )
+ {
+ elnk = ( PEHCI_ELEM_LINKS )RemoveHeadList( &lh );
+ elem_pool_free_elem( elnk );
+ }
+ return TRUE;
+}
+LONG
+elem_pool_get_type(
+PEHCI_ELEM_POOL pool
+)
+{
+ if( pool == NULL )
+ return -1;
+ return ( pool->flags & 0xf );
+}
+
+BOOL
+elem_pool_expand_pool(
+PEHCI_ELEM_POOL pool,
+LONG elem_count
+)
+{
+ LONG elem_cnt_list, list_count, i, j;
+ INIT_ELEM_LIST_CONTEXT init_ctx;
+
+ if( pool == NULL || elem_count <= 0 || elem_count > EHCI_MAX_ELEMS_POOL )
+ return FALSE;
+
+ init_ctx.pool = pool;
+ init_ctx.padapter = pool->elem_lists[ 0 ]->padapter;
+
+ elem_cnt_list = pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] );
+ list_count = ( elem_count + elem_cnt_list - 1 ) / elem_cnt_list;
+ get_max_lists_count( pool, i );
+
+ if( list_count + pool->list_count > i )
+ return FALSE;
+
+ for( i = pool->list_count; i < list_count + pool->list_count; i++ )
+ {
+ pool->elem_lists[ i ] = usb_alloc_mem( NonPagedPool, sizeof( EHCI_ELEM_LIST ) );
+ if( elem_list_init_elem_list( pool->elem_lists[ i ], pool->flags, &init_ctx, 0 ) == FALSE )
+ break;
+ }
+
+ if( i < list_count + pool->list_count )
+ {
+ // undo all we have done
+ for( j = pool->list_count; j < pool->list_count + i; j++ )
+ {
+ pool->elem_lists[ j ]->destroy_list( pool->elem_lists[ j ] );
+ usb_free_mem( pool->elem_lists[ j ] );
+ pool->elem_lists[ j ] = NULL;
+ }
+ return FALSE;
+ }
+
+ // update pool
+ pool->free_count += elem_cnt_list * list_count;
+ pool->list_count += list_count;
+ return TRUE;
+}
+
+BOOL
+elem_pool_collect_garbage(
+PEHCI_ELEM_POOL pool
+)
+{
+ LONG i, j, k, fl;
+ LONG free_elem_lists[ EHCI_MAX_LISTS_POOL - 1 ];
+ PEHCI_ELEM_LIST pel;
+
+ if( pool == NULL )
+ return FALSE;
+
+ for( i = 1, fl = 0; i < pool->list_count; i++ )
+ {
+ if( pool->elem_lists[ i ]->get_ref( pool->elem_lists[ i ] ) == 0 )
+ {
+ free_elem_lists[ fl++ ] = i;
+ }
+ }
+ for( j = fl - 1; j >= 0; j-- )
+ {
+ pel = pool->elem_lists[ free_elem_lists[ j ] ];
+ pel->destroy_list( pel );
+ usb_free_mem( pel );
+
+ for( k = free_elem_lists[ j ] + 1; k < pool->list_count; k++ )
+ {
+ // shrink the elem_lists
+ pool->elem_lists[ k - 1 ] = pool->elem_lists[ k ];
+ }
+ pool->elem_lists[ k ] = NULL;
+ pel = pool->elem_lists[ 0 ];
+ pool->free_count -= pel->get_total_count( pel );
+ pool->list_count --;
+ }
+ return TRUE;
+}
+
+BOOL
+elem_pool_can_transfer(
+PEHCI_ELEM_POOL pool,
+LONG td_count
+)
+{
+ LONG i;
+ if( pool == NULL || td_count <= 0 )
+ return FALSE;
+ get_max_lists_count( pool, i );
+ if( ( i - pool->list_count )
+ * pool->elem_lists[ 0 ]->get_total_count( pool->elem_lists[ 0 ] )
+ + pool->free_count < td_count )
+ return FALSE;
+ return TRUE;
+}
+
+//----------------------------------------------------------
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c
new file mode 100644
index 00000000000..f15c55d7f8b
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.c
@@ -0,0 +1,2076 @@
+/**
+ * gendrv.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//this driver is part of the dev manager responsible to manage non-driver device
+
+#include "td.h"
+#include "ntddk.h"
+#include "umss.h"
+#include "usb.h"
+#include "hub.h"
+#include "debug.h"
+#include "gendrv.h"
+#include "stdio.h"
+
+#define if_dev( dev_obj ) \
+( ( ( ( PGENDRV_DEVICE_EXTENSION)dev_obj->DeviceExtension )->pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) != 0 )
+
+#define GENDRV_EXIT_DISPATCH( dev_OBJ, staTUS, iRp ) \
+{\
+ iRp->IoStatus.Status = staTUS;\
+ if( staTUS != STATUS_PENDING)\
+ {\
+ IoCompleteRequest( iRp, IO_NO_INCREMENT);\
+ return staTUS;\
+ }\
+ IoMarkIrpPending( iRp );\
+ IoStartPacket( dev_OBJ, iRp, NULL, gendrv_cancel_queued_irp ); \
+ return STATUS_PENDING;\
+}
+
+#define GENDRV_COMPLETE_START_IO( dev_OBJ, staTUS, iRP ) \
+{\
+ iRP->IoStatus.Status = staTUS;\
+ if( staTUS != STATUS_PENDING )\
+ {\
+ IoStartNextPacket( dev_OBJ, TRUE );\
+ IoCompleteRequest( iRP, IO_NO_INCREMENT );\
+ }\
+ return;\
+}
+
+extern POBJECT_TYPE *IoDriverObjectType;
+
+extern VOID
+disp_urb_completion(
+PURB purb,
+PVOID context
+);
+
+
+VOID
+disp_noio_urb_completion(
+PURB purb,
+PVOID context
+);
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwLoadDriver(
+IN PUNICODE_STRING DriverServiceName
+);
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwClose(
+IN HANDLE Handle
+);
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ObOpenObjectByName(
+IN POBJECT_ATTRIBUTES ObjectAttributes,
+IN POBJECT_TYPE ObjectType OPTIONAL,
+IN KPROCESSOR_MODE AccessMode,
+IN OUT PACCESS_STATE AccessState OPTIONAL,
+IN ACCESS_MASK DesiredAccess OPTIONAL,
+IN OUT PVOID ParseContext OPTIONAL,
+OUT PHANDLE Handle
+);
+
+BOOL
+gendrv_if_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+VOID
+gendrv_set_cfg_completion(
+PURB purb,
+PVOID context
+);
+
+BOOL
+gendrv_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+gendrv_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+gendrv_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+VOID
+gendrv_startio(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+);
+
+VOID
+gendrv_cancel_queued_irp(
+PDEVICE_OBJECT pdev_obj,
+PIRP pirp
+);
+
+VOID
+gendrv_release_ext_drvr_entry(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+PGENDRV_EXT_DRVR_ENTRY pentry
+);
+
+VOID
+gendrv_clean_up_queued_irps(
+PDEVICE_OBJECT dev_obj
+);
+
+PGENDRV_EXT_DRVR_ENTRY
+gendrv_alloc_ext_drvr_entry(
+PGENDRV_DRVR_EXTENSION pdrvr_ext
+);
+
+PDRIVER_OBJECT
+gendrv_open_ext_driver(
+PUNICODE_STRING unicode_string
+);
+
+NTSTATUS
+gendrv_get_key_value(
+IN HANDLE KeyHandle,
+IN PWSTR ValueName,
+OUT PKEY_VALUE_FULL_INFORMATION *Information
+);
+
+NTSTATUS
+gendrv_open_reg_key(
+OUT PHANDLE handle,
+IN HANDLE base_handle OPTIONAL,
+IN PUNICODE_STRING keyname,
+IN ACCESS_MASK desired_access,
+IN BOOLEAN create
+);
+
+BOOL
+gendrv_do_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle,
+BOOL is_if
+);
+
+BOOL
+gendrv_do_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle,
+BOOL is_if
+);
+
+NTSTATUS
+gendrv_send_pnp_msg(
+ULONG msg,
+PDEVICE_OBJECT pdev_obj,
+PVOID pctx
+);
+
+BOOL
+gendrv_delete_device(
+PUSB_DEV_MANAGER dev_mgr,
+PDEVICE_OBJECT dev_obj
+);
+
+PDEVICE_OBJECT
+gendrv_create_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER gen_drvr,
+DEV_HANDLE dev_handle
+);
+
+PDRIVER_OBJECT
+gendrv_load_ext_drvr(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+PUSB_DESC_HEADER pdesc
+);
+
+PDRIVER_OBJECT
+gendrv_find_drvr_by_key(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+ULONG key
+);
+
+ULONG
+gendrv_make_key(
+PUSB_DESC_HEADER pdesc
+);
+
+BOOL
+gendrv_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
+ pdriver->driver_desc.release_num = 0x100; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 0; // Configuration Value
+ pdriver->driver_desc.if_num = 0; // Interface Number
+ pdriver->driver_desc.if_class = 0xff; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0xff; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0xff; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB generic dev driver"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_VENDOR_SPEC;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( GENDRV_DRVR_EXTENSION ) );
+ pdriver->driver_ext_size = sizeof( GENDRV_DRVR_EXTENSION );
+
+ RtlZeroMemory( pdriver->driver_ext, pdriver->driver_ext_size );
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext;
+
+ // InitializeListHead( &pdrvr_ext->dev_list );
+ InitializeListHead( &pdrvr_ext->ext_drvr_list );
+ pdrvr_ext->ext_drvr_count = 0;
+ ExInitializeFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = gendrv_connect;
+ pdriver->disp_tbl.dev_disconnect = gendrv_disconnect;
+ pdriver->disp_tbl.dev_stop = gendrv_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ return TRUE;
+}
+
+BOOL
+gendrv_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ return gendrv_if_driver_destroy( dev_mgr, pdriver );
+}
+
+BOOL
+gendrv_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+)
+{
+ PURB purb;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ PUCHAR buf;
+ LONG credit, i, j, match;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ if( param == NULL || dev_handle == 0 )
+ return FALSE;
+
+ dev_mgr = param->dev_mgr;
+
+ // let's set the configuration
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return FALSE;
+
+ buf = usb_alloc_mem( NonPagedPool, 512 );
+ if( buf == NULL )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): can not alloc buf\n" ) );
+ usb_free_mem( purb );
+ return FALSE;
+ }
+
+ // before we set the configuration, let's search to find if there
+ // exist interfaces we supported
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+ purb->endp_handle = dev_handle | 0xffff;
+ purb->data_buffer = buf;
+ purb->data_length = 512;
+ purb->completion = NULL; // this is an immediate request, no completion required
+ purb->context = NULL;
+ purb->reference = 0;
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = USB_DT_CONFIG << 8;
+ psetup->wIndex = 0;
+ psetup->wLength = 512;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ TRAP();
+ usb_free_mem( buf );
+ usb_free_mem( purb );
+ return FALSE;
+ }
+
+ // check the config desc valid
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )buf;
+ if( pconfig_desc->wTotalLength > 512 )
+ {
+ usb_free_mem( buf );
+ usb_free_mem( purb );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): error, bad configuration desc\n" ) );
+ return FALSE;
+ }
+
+ i = pconfig_desc->bConfigurationValue;
+ usb_free_mem( buf );
+ buf = NULL;
+
+ //set the configuration
+ urb_init( purb );
+ purb->endp_handle = dev_handle | 0xffff;
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->completion = gendrv_set_cfg_completion;
+ purb->context = dev_mgr;
+ purb->reference = ( ULONG )param->pdriver;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_CONFIGURATION;
+ psetup->wValue = ( USHORT ) i;
+ psetup->wIndex = 0;
+ psetup->wLength = 0;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_connect(): start config the device, cfgval=%d\n", i ) );
+ status = usb_submit_urb( dev_mgr, purb );
+
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+
+ if( status == STATUS_SUCCESS )
+ return TRUE;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL
+gendrv_event_select_driver(
+PUSB_DEV pdev, //always null. we do not use this param
+ULONG event,
+ULONG context,
+ULONG param
+)
+{
+ //
+ // try to search the registry to find one driver.
+ // if found, create the PDO, load the driver.
+ // and call its AddDevice.
+ //
+ LONG i;
+ NTSTATUS status;
+ PUSB_DRIVER pdrvr;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ PDEVICE_OBJECT pdev_obj;
+ PDRIVER_OBJECT pdrvr_obj;
+ HANDLE handle;
+ PLIST_ENTRY pthis, pnext;
+
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_event_select_driver(): entering...\n" ) );
+
+ pdrvr = ( PUSB_DRIVER )param;
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ];
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext;
+
+ //
+ // well, let's do the hard work to see if there is a class driver
+ // for this device.
+ // in the event routine, we have no need to check if the device is zomb or
+ // not, it must be alive there.
+ //
+ i = gendrv_make_key( ( PUSB_DESC_HEADER )pdev->pusb_dev_desc );
+ if( i == -1 )
+ {
+ return FALSE;
+ }
+
+ pdrvr_obj = gendrv_find_drvr_by_key( pdrvr_ext, ( ULONG )i );
+ if( !pdrvr_obj )
+ {
+ if( ( pdrvr_obj = gendrv_load_ext_drvr( pdrvr_ext, ( PUSB_DESC_HEADER )pdev->pusb_dev_desc ) ) == NULL )
+ return FALSE;
+ }
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ pdev_obj = gendrv_create_device( dev_mgr, pdrvr, usb_make_handle( pdev->dev_id, 0, 0 ) );
+ if( pdev_obj == NULL )
+ {
+ goto ERROR_OUT;
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ||
+ dev_mgr_set_driver( dev_mgr, usb_make_handle( pdev->dev_id, 0, 0 ), pdrvr, pdev ) == FALSE )
+ {
+ unlock_dev( pdev, FALSE );
+ gendrv_delete_device( dev_mgr, pdev_obj );
+ goto ERROR_OUT;
+ }
+
+ if( pdev->usb_config )
+ {
+ pdev->dev_obj = pdev_obj;
+ }
+
+ unlock_dev( pdev, FALSE );
+
+ pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev_obj->DeviceExtension;
+ pdev_ext->desc_buf = usb_alloc_mem( NonPagedPool, 512 );
+ RtlCopyMemory( pdev_ext->desc_buf, pconfig_desc, 512 );
+
+ // insert the device to the dev_list
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ListFirst( &pdrvr_ext->ext_drvr_list, pthis );
+ pentry = NULL;
+ while( pthis )
+ {
+ pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis;
+ if( pentry->pext_drvr == pdrvr_obj )
+ break;
+ ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext );
+ pthis = pnext;
+ pentry = NULL;
+ }
+ ASSERT( pentry );
+ InsertTailList( &pentry->dev_list, &pdev_ext->dev_obj_link );
+ pdev_ext->ext_drvr_entry = pentry;
+ pentry->ref_count++;
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ // notify the class driver, some device comes
+ gendrv_send_pnp_msg( GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj );
+ usb_unlock_dev( pdev );
+ return TRUE;
+
+ERROR_OUT:
+
+ usb_unlock_dev( pdev );
+ return FALSE;
+}
+
+VOID
+gendrv_set_cfg_completion(
+PURB purb,
+PVOID context
+)
+{
+ DEV_HANDLE dev_handle;
+ PUSB_DEV_MANAGER dev_mgr;
+ PWORK_QUEUE_ITEM pwork_item;
+ PUSB_DRIVER pdriver;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_EVENT pevent;
+ USE_IRQL;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ dev_handle = purb->endp_handle & ~0xffff;
+ dev_mgr = ( PUSB_DEV_MANAGER ) context;
+ pdriver = ( PUSB_DRIVER )purb->reference;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ usb_free_mem( purb );
+ purb = NULL;
+
+ // set the dev state
+ status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ return;
+ }
+ usb_unlock_dev( pdev ); // safe to release the pdev ref since we are in urb completion
+
+
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ return;
+ }
+
+ if( dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ return;
+ }
+
+ //transit the state to configured
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ }
+
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->event = USB_EVENT_DEFAULT;
+ pevent->pdev = pdev;
+ pevent->context = 0;
+ pevent->param = ( ULONG )pdriver;
+ pevent->pnext = 0; //vertical queue for serialized operation
+ pevent->process_event = gendrv_event_select_driver;
+ pevent->process_queue = event_list_default_process_queue;
+
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+
+ return;
+}
+
+
+BOOL
+gendrv_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ if( dev_mgr == NULL )
+ return FALSE;
+ return gendrv_do_stop( dev_mgr, dev_handle, FALSE );
+}
+
+BOOL
+gendrv_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ if( dev_mgr == NULL )
+ return FALSE;
+ return gendrv_do_disconnect( dev_mgr, dev_handle, FALSE );
+}
+
+BOOL
+gendrv_build_reg_string(
+PUSB_DESC_HEADER pdesc,
+PUNICODE_STRING pus
+)
+{
+
+ CHAR desc_str[ 128 ];
+ STRING atemp;
+
+ if( pdesc == NULL || pus == NULL )
+ return FALSE;
+
+ if( pdesc->bDescriptorType == USB_DT_DEVICE )
+ {
+ PUSB_DEVICE_DESC pdev_desc;
+ pdev_desc = ( PUSB_DEVICE_DESC )pdesc;
+ sprintf( desc_str, "%sv_%04x&p_%04x",
+ "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\device\\",
+ pdev_desc->idVendor,
+ pdev_desc->idProduct );
+ }
+ else if( pdesc->bDescriptorType == USB_DT_INTERFACE )
+ {
+ PUSB_INTERFACE_DESC pif_desc;
+ pif_desc = ( PUSB_INTERFACE_DESC )pdesc;
+ sprintf( desc_str, "%sc_%04x&s_%04x&p_%04x",
+ "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ehci\\interface\\",
+ pif_desc->bInterfaceClass,
+ pif_desc->bInterfaceSubClass,
+ pif_desc->bInterfaceProtocol );
+ }
+ else
+ return FALSE;
+
+ RtlInitString( &atemp, desc_str );
+ RtlAnsiStringToUnicodeString( pus, &atemp, TRUE );
+ return TRUE;
+}
+
+ULONG
+gendrv_make_key(
+PUSB_DESC_HEADER pdesc
+)
+{
+ PUSB_DEVICE_DESC pdev_desc;
+ PUSB_INTERFACE_DESC pif_desc;
+
+ if( pdesc == NULL )
+ return ( ULONG )-1;
+ if( pdesc->bDescriptorType == USB_DT_DEVICE )
+ {
+ pdev_desc = ( PUSB_DEVICE_DESC )pdesc;
+ return ( ( ( ( ULONG )pdev_desc->idVendor ) << 16 ) | pdev_desc->idProduct );
+ }
+ else if( pdesc->bDescriptorType == USB_DT_INTERFACE )
+ {
+ pif_desc = ( PUSB_INTERFACE_DESC )pdesc;
+ return ( ( ( ( ULONG )pif_desc->bInterfaceClass ) << 16 ) | \
+ ( ( ( ULONG )pif_desc->bInterfaceSubClass ) << 8 ) | \
+ ( ( ULONG )pif_desc->bInterfaceProtocol ) );
+ }
+ return ( ULONG )-1;
+}
+
+PDRIVER_OBJECT
+gendrv_find_drvr_by_key(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+ULONG key
+)
+{
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+ PLIST_ENTRY pthis, pnext;
+ if( pdrvr_ext == NULL || key == ( ULONG )-1 )
+ return NULL;
+
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ListFirst( &pdrvr_ext->ext_drvr_list, pthis );
+ while( pthis )
+ {
+ pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis;
+ if( pentry->drvr_key == key )
+ {
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ return pentry->pext_drvr;
+ }
+ ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext );
+ pthis = pnext;
+ }
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ return NULL;
+}
+
+PDRIVER_OBJECT
+gendrv_load_ext_drvr(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+PUSB_DESC_HEADER pdesc
+)
+{
+ PDRIVER_OBJECT pdrvr_obj;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+ UNICODE_STRING usz, svc_name, svc_key, utemp;
+ PKEY_VALUE_FULL_INFORMATION val_info;
+ PWCHAR val_buf;
+ HANDLE handle;
+ NTSTATUS status;
+
+ if( pdrvr_ext == NULL || pdesc == NULL )
+ return NULL;
+
+ // try to search and load driver from outside
+ handle = NULL;
+ RtlZeroMemory( &svc_key, sizeof( svc_key ) );
+ val_info = NULL;
+ RtlInitUnicodeString( &usz, L"" );
+ gendrv_build_reg_string( pdesc, &usz );
+ if( gendrv_open_reg_key( &handle, NULL, &usz, KEY_READ, FALSE ) != STATUS_SUCCESS )
+ {
+ goto ERROR_OUT;
+ }
+ if( gendrv_get_key_value( handle, L"service", &val_info ) != STATUS_SUCCESS )
+ {
+ goto ERROR_OUT;
+ }
+
+ if( val_info->DataLength > 32 )
+ goto ERROR_OUT;
+
+ val_buf = ( PWCHAR )( ( ( PBYTE )val_info ) + val_info->DataOffset );
+ svc_key.Length = 0, svc_key.MaximumLength = 255;
+ svc_key.Buffer = usb_alloc_mem( NonPagedPool, 256 );
+
+ RtlInitUnicodeString( &utemp, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\" );
+ RtlAppendUnicodeStringToString( &svc_key, &utemp);
+ RtlInitUnicodeString( &svc_name, val_buf );
+ RtlAppendUnicodeStringToString( &svc_key, &svc_name );
+
+ status = ZwLoadDriver( &svc_key );
+ if( status != STATUS_SUCCESS )
+ goto ERROR_OUT;
+
+ svc_key.Length = 0;
+ RtlZeroMemory( svc_key.Buffer, 128 );
+ RtlInitUnicodeString( &svc_key, L"\\Driver\\" );
+ RtlAppendUnicodeStringToString( &svc_key, &svc_name );
+ pdrvr_obj = gendrv_open_ext_driver( &svc_key );
+ if( pdrvr_obj == NULL )
+ goto ERROR_OUT;
+
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ // insert the driver to the drvr list
+ pentry = gendrv_alloc_ext_drvr_entry( pdrvr_ext );
+ if( pentry == NULL )
+ {
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ObDereferenceObject( pdrvr_obj );
+ goto ERROR_OUT;
+ }
+ pentry->pext_drvr = pdrvr_obj;
+ InsertTailList( &pdrvr_ext->ext_drvr_list, &pentry->drvr_link );
+ pdrvr_ext->ext_drvr_count++;
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ZwClose( handle );
+ return pdrvr_obj;
+
+ERROR_OUT:
+ RtlFreeUnicodeString( &usz );
+ if( val_info != NULL )
+ {
+ usb_free_mem( val_info );
+ val_info = NULL;
+ }
+ if( svc_key.Buffer )
+ usb_free_mem( svc_key.Buffer );
+
+ if( handle )
+ ZwClose( handle );
+
+ return NULL;
+}
+
+VOID
+gendrv_release_drvr(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+PDRIVER_OBJECT pdrvr_obj
+)
+{
+ PLIST_ENTRY pthis, pnext;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+
+ if( pdrvr_ext == NULL || pdrvr_obj == NULL )
+ return;
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ListFirst( &pdrvr_ext->ext_drvr_list, pthis );
+ while( pthis )
+ {
+ pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis;
+ if( pentry->pext_drvr == pdrvr_obj )
+ {
+ ASSERT( pentry->ref_count );
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ return;
+ }
+ ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext );
+ pthis = pnext;
+ }
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+}
+
+NTSTATUS
+gendrv_send_pnp_msg(
+ULONG msg,
+PDEVICE_OBJECT pdev_obj,
+PVOID pctx
+)
+{
+ PDRIVER_EXTENSION pwin_drvr_ext;
+ if( pdev_obj == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ switch( msg )
+ {
+ case GENDRV_MSG_ADDDEVICE:
+ {
+ PDRIVER_OBJECT pdrvr_obj;
+ if( pctx == NULL )
+ return STATUS_INVALID_PARAMETER;
+ pdrvr_obj = (PDRIVER_OBJECT )pctx;
+ if( pdrvr_obj->DriverExtension )
+ {
+ return pdrvr_obj->DriverExtension->AddDevice( pdrvr_obj, &pdev_obj );
+ }
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ case GENDRV_MSG_STOPDEVICE:
+ case GENDRV_MSG_DISCDEVICE:
+ {
+ NTSTATUS status;
+ IO_STACK_LOCATION *irpstack;
+ IRP *irp;
+ // IRP_MJ_PNP_POWER
+ irp = IoAllocateIrp( 2, FALSE );
+ if( irp == NULL )
+ return STATUS_NO_MEMORY;
+
+ irpstack = IoGetNextIrpStackLocation(irp);
+ irpstack->MajorFunction = IRP_MJ_PNP_POWER;
+ irpstack->MinorFunction =
+ ( msg == GENDRV_MSG_STOPDEVICE ) ? IRP_MN_STOP_DEVICE : IRP_MN_REMOVE_DEVICE;
+ status = IoCallDriver( pdev_obj, irp );
+ ASSERT( status != STATUS_PENDING );
+ status = irp->IoStatus.Status;
+ IoFreeIrp( irp );
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ }
+ return STATUS_INVALID_PARAMETER;
+}
+
+
+BOOL
+gendrv_if_connect(
+PCONNECT_DATA params,
+DEV_HANDLE if_handle
+)
+{
+ //
+ // try to search the registry to find one driver.
+ // if found, create the PDO, load the driver.
+ // and call its AddDevice.
+ //
+ LONG if_idx, i;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_DRIVER pdrvr;
+ PUSB_INTERFACE_DESC pif_desc;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_DEV_MANAGER dev_mgr;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+
+ PDEVICE_OBJECT pdev_obj;
+ PDRIVER_OBJECT pdrvr_obj;
+ HANDLE handle;
+ PLIST_ENTRY pthis, pnext;
+ USE_IRQL;
+
+ pdev = NULL;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_if_connect(): entering...\n" ) );
+
+ if( params == NULL )
+ return FALSE;
+
+ dev_mgr = params->dev_mgr;
+ pdrvr = params->pdriver;
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext;
+
+ status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ goto ERROR_OUT;
+ }
+ // obtain the pointer to the config desc, the dev won't go away in this routine
+ pconfig_desc = pdev->usb_config->pusb_config_desc; //
+ usb_unlock_dev( pdev );
+ pdev = NULL;
+
+ if_idx = if_idx_from_handle( if_handle );
+ pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] );
+
+ for( i = 0; i < if_idx; i++ )
+ {
+ //search for our if
+ if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE )
+ break;
+ }
+ if( pif_desc == NULL )
+ return FALSE;
+
+ //
+ // well, let's do the hard work to see if there is a class driver
+ // for this device.
+ //
+ i = gendrv_make_key( ( PUSB_DESC_HEADER )pif_desc );
+ if( i == -1 )
+ {
+ return FALSE;
+ }
+
+ pdrvr_obj = gendrv_find_drvr_by_key( pdrvr_ext, ( ULONG )i );
+ if( !pdrvr_obj )
+ {
+ if( ( pdrvr_obj = gendrv_load_ext_drvr( pdrvr_ext, ( PUSB_DESC_HEADER )pif_desc ) ) == NULL )
+ return FALSE;
+ }
+
+
+ pdev_obj = gendrv_create_device( dev_mgr, pdrvr, if_handle );
+ if( pdev_obj == NULL )
+ {
+ goto ERROR_OUT;
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ||
+ dev_mgr_set_if_driver( dev_mgr, if_handle, pdrvr, pdev ) == FALSE )
+ {
+ unlock_dev( pdev, FALSE );
+ gendrv_delete_device( dev_mgr, pdev_obj );
+ goto ERROR_OUT;
+ }
+
+ if( pdev->usb_config )
+ {
+ pdev->usb_config->interf[ if_idx ].if_ext = pdev_obj;
+ pdev->usb_config->interf[ if_idx ].if_ext_size = 0;
+ }
+
+ unlock_dev( pdev, FALSE );
+
+ pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev_obj->DeviceExtension;
+ pdev_ext->desc_buf = usb_alloc_mem( NonPagedPool, 512 );
+ RtlCopyMemory( pdev_ext->desc_buf, pconfig_desc, 512 );
+ pdev_ext->if_ctx.pif_desc = ( PUSB_INTERFACE_DESC )&pdev_ext->desc_buf[ ( PBYTE )pif_desc - ( PBYTE )pconfig_desc ];
+
+ // insert the device to the dev_list
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ ListFirst( &pdrvr_ext->ext_drvr_list, pthis );
+ pentry = NULL;
+ while( pthis )
+ {
+ pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis;
+ if( pentry->pext_drvr == pdrvr_obj )
+ break;
+ ListNext( &pdrvr_ext->ext_drvr_list, pthis, pnext );
+ pthis = pnext;
+ pentry = NULL;
+ }
+ ASSERT( pentry );
+ InsertTailList( &pentry->dev_list, &pdev_ext->dev_obj_link );
+ pdev_ext->ext_drvr_entry = pentry;
+ pentry->ref_count++;
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ // notify the class driver, some device comes
+ gendrv_send_pnp_msg( GENDRV_MSG_ADDDEVICE, pdev_obj, pdrvr_obj );
+ usb_unlock_dev( pdev );
+ return TRUE;
+
+ERROR_OUT:
+
+ usb_unlock_dev( pdev );
+ return FALSE;
+}
+
+BOOL
+gendrv_do_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle,
+BOOL is_if
+)
+{
+ PUSB_DEV pdev;
+ PDEVICE_OBJECT pdev_obj;
+ ULONG if_idx;
+
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ // clean up the irps
+ if_idx = if_idx_from_handle( dev_handle );
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS )
+ {
+ return FALSE;
+ }
+ if( is_if && pdev->usb_config )
+ pdev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext;
+ else
+ pdev_obj = pdev->dev_obj;
+
+ gendrv_clean_up_queued_irps( pdev_obj );
+ usb_unlock_dev( pdev );
+
+ // send message to class drivers.
+ gendrv_send_pnp_msg( GENDRV_MSG_STOPDEVICE, pdev_obj, NULL );
+
+ return TRUE;
+}
+
+BOOL
+gendrv_if_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ return gendrv_do_stop( dev_mgr, dev_handle, TRUE );
+}
+
+BOOL
+gendrv_do_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle,
+BOOL is_if
+)
+{
+ PUSB_DEV pdev;
+ PDEVICE_OBJECT dev_obj;
+ NTSTATUS status;
+ PUSB_DRIVER pdrvr;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+ PLIST_ENTRY pthis;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ ULONG if_idx;
+
+ status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev );
+ if( pdev == NULL )
+ {
+ return FALSE;
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ // must be a bug
+ TRAP();
+ }
+ if_idx = if_idx_from_handle( if_handle );
+ if( pdev->usb_config )
+ {
+ if( is_if )
+ {
+ pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv;
+ dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext;
+ }
+ else
+ {
+ pdrvr = pdev->dev_driver;
+ dev_obj = pdev->dev_obj;
+ }
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext;
+ pdev_ext = ( PGENDRV_DEVICE_EXTENSION ) dev_obj->DeviceExtension;
+ }
+ else
+ TRAP();
+ pdev = NULL;
+
+ // remove the device from the list
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ RemoveEntryList( &pdev_ext->dev_obj_link );
+ pdev_ext->ext_drvr_entry->ref_count--;
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ // send message to class driver
+ gendrv_send_pnp_msg( GENDRV_MSG_DISCDEVICE, dev_obj, NULL );
+ // delete the device object
+ gendrv_delete_device( dev_mgr, dev_obj );
+ return TRUE;
+}
+
+BOOL
+gendrv_if_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle
+)
+{
+ return gendrv_do_disconnect( dev_mgr, if_handle, TRUE );
+}
+
+BOOL
+gendrv_if_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_IF_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0x0000; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0x0000; // USB Product ID.
+ pdriver->driver_desc.release_num = 0x100; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 0; // Configuration Value
+ pdriver->driver_desc.if_num = 0; // Interface Number
+ pdriver->driver_desc.if_class = 0x0; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0x0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0x0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB generic interface driver"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = 0;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ //we have no extra data sturcture currently
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = gendrv_if_connect;
+ pdriver->disp_tbl.dev_disconnect = gendrv_if_disconnect;
+ pdriver->disp_tbl.dev_stop = gendrv_if_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( GENDRV_DRVR_EXTENSION ) );
+ pdriver->driver_ext_size = sizeof( GENDRV_DRVR_EXTENSION );
+
+ RtlZeroMemory( pdriver->driver_ext, pdriver->driver_ext_size );
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext;
+
+ // InitializeListHead( &pdrvr_ext->dev_list );
+ InitializeListHead( &pdrvr_ext->ext_drvr_list );
+ pdrvr_ext->ext_drvr_count = 0;
+ ExInitializeFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ return TRUE;
+}
+
+BOOL
+gendrv_if_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ LONG i;
+ PLIST_ENTRY pthis, pnext;
+ PGENDRV_EXT_DRVR_ENTRY pentry;
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ if( pdriver->driver_ext )
+ {
+ // should we lock it?
+ // ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdriver->driver_ext;
+ if( pdrvr_ext->ext_drvr_count )
+ {
+ while( IsListEmpty( &pdrvr_ext->ext_drvr_list ) )
+ {
+ pthis = RemoveHeadList( &pdrvr_ext->ext_drvr_list );
+ pentry = ( PGENDRV_EXT_DRVR_ENTRY )pthis;
+ if( pentry->pext_drvr )
+ {
+ if( pentry->ref_count )
+ {
+ // FIXME: really fail?
+ continue;
+ }
+ ObDereferenceObject( pentry->pext_drvr );
+ gendrv_release_ext_drvr_entry( pdrvr_ext, pentry );
+ }
+ }
+ pdrvr_ext->ext_drvr_count = 0;
+ }
+
+ usb_free_mem( pdriver->driver_ext );
+ pdriver->driver_ext = NULL;
+ pdriver->driver_ext_size = 0;
+ }
+ return TRUE;
+}
+
+PDRIVER_OBJECT
+gendrv_open_ext_driver(
+PUNICODE_STRING unicode_string
+)
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES oa;
+ HANDLE drvr_handle;
+ UNICODE_STRING oname;
+ PDRIVER_OBJECT pdrvr;
+
+ RtlZeroMemory( &oa, sizeof( oa ) );
+ oa.Length = sizeof( oa );
+ oa.ObjectName = &oname;
+ oa.Attributes = OBJ_CASE_INSENSITIVE;
+ RtlInitUnicodeString( &oname, L"" );
+ RtlAppendUnicodeStringToString( &oname, unicode_string );
+
+ status = ObOpenObjectByName( &oa,
+ *IoDriverObjectType, // object type
+ KernelMode, // access mode
+ NULL, // access state
+ FILE_READ_DATA, // STANDARD_RIGHTS_READ, access right
+ NULL,
+ &drvr_handle );
+
+ if( status != STATUS_SUCCESS )
+ {
+ return NULL;
+ }
+ ObReferenceObjectByHandle( drvr_handle,
+ FILE_READ_DATA,
+ *IoDriverObjectType,
+ KernelMode,
+ &pdrvr,
+ NULL // OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
+ );
+ ZwClose( drvr_handle );
+ return pdrvr;
+}
+
+BOOL
+gendrv_close_ext_driver( PDRIVER_OBJECT pdrvr )
+{
+ if( pdrvr == NULL )
+ return FALSE;
+ ObDereferenceObject( pdrvr );
+ return TRUE;
+}
+
+NTSTATUS
+gendrv_dispatch(
+PDEVICE_OBJECT dev_obj,
+PIRP irp
+)
+{
+ IO_STACK_LOCATION *irpstack;
+ PUSB_DEV_MANAGER dev_mgr;
+ PDEVEXT_HEADER ext_hdr;
+ NTSTATUS status;
+
+ if( dev_obj == NULL || irp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ ext_hdr = dev_obj->DeviceExtension;
+ dev_mgr = ext_hdr->dev_mgr;
+
+ irpstack = IoGetNextIrpStackLocation(irp);
+ switch( irpstack->MajorFunction )
+ {
+ case IRP_MJ_PNP_POWER:
+ {
+ irp->IoStatus.Information = 0;
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp );
+ }
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ {
+ status = STATUS_NOT_SUPPORTED;
+ if( irpstack->MinorFunction == IOCTL_SUBMIT_URB_RD ||
+ irpstack->MinorFunction == IOCTL_SUBMIT_URB_WR ||
+ irpstack->MinorFunction == IOCTL_SUBMIT_URB_NOIO )
+ {
+ PURB purb;
+ DEV_HANDLE endp_handle;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+
+ pdev_ext = dev_obj->DeviceExtension;
+ if( irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) )
+ {
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ purb = ( PURB )irp->AssociatedIrp.SystemBuffer;
+ endp_handle = purb->endp_handle;
+ if( purb->data_buffer == NULL || purb->data_length == 0 )
+ {
+ if( irpstack->MinorFunction != IOCTL_SUBMIT_URB_NOIO )
+ {
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+ }
+ if( !default_endp_handle( endp_handle ) )
+ {
+ //no permit to other interface if interface dev
+ if( if_dev( dev_obj ) &&
+ if_idx_from_handle( endp_handle ) != pdev_ext->if_ctx.if_idx )
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_PENDING, irp );
+ }
+ else if( irpstack->MinorFunction == IOCTL_GET_DEV_DESC )
+ {
+ // this is a synchronous call, route to dev_mgr_dispatch
+ return dev_mgr_dispatch( dev_mgr, irp );
+ }
+ else if( irpstack->MinorFunction == IOCTL_GET_DEV_HANDLE )
+ {
+ PUCHAR user_buffer;
+ ULONG user_buffer_length;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ pdev_ext = dev_obj->DeviceExtension;
+ if( irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) )
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+
+ *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle;
+ irp->IoStatus.Information = sizeof( LONG );
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp );
+ }
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_NOT_SUPPORTED, irp );
+ }
+ case IRP_MJ_DEVICE_CONTROL:
+ {
+ status = STATUS_NOT_SUPPORTED;
+ if( irpstack->MinorFunction == IOCTL_SUBMIT_URB_RD ||
+ irpstack->MinorFunction == IOCTL_SUBMIT_URB_WR ||
+ irpstack->MinorFunction == IOCTL_SUBMIT_URB_NOIO )
+ {
+ PURB purb;
+ DEV_HANDLE endp_handle;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+
+ pdev_ext = dev_obj->DeviceExtension;
+ if( irpstack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) )
+ {
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ purb = ( PURB )irp->AssociatedIrp.SystemBuffer;
+ endp_handle = purb->endp_handle;
+ if( !default_endp_handle( endp_handle ) )
+ {
+ //no permit to other interface if interface dev
+ if( if_dev( dev_obj ) &&
+ if_idx_from_handle( endp_handle ) != pdev_ext->if_ctx.if_idx )
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_PENDING, irp );
+ }
+ else if( irpstack->MinorFunction == IOCTL_GET_DEV_DESC )
+ {
+ // this is a synchronous call, route to dev_mgr_dispatch
+ return dev_mgr_dispatch( dev_mgr, irp );
+ }
+ else if( irpstack->MinorFunction == IOCTL_GET_DEV_HANDLE )
+ {
+ PUCHAR user_buffer;
+ ULONG user_buffer_length;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ pdev_ext = dev_obj->DeviceExtension;
+ if( irpstack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) )
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_INVALID_PARAMETER, irp );
+
+ *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle;
+ irp->IoStatus.Information = sizeof( LONG );
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_SUCCESS, irp );
+ }
+ GENDRV_EXIT_DISPATCH( dev_obj, STATUS_NOT_SUPPORTED, irp );
+ }
+ }
+ irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ irp->IoStatus.Information = 0;
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+
+ return STATUS_NOT_SUPPORTED;
+}
+BOOL
+gendrv_init_dev_ext_hdr(
+PDEVICE_OBJECT dev_obj,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ PDEVEXT_HEADER dev_hdr;
+ if( dev_obj == NULL || dev_mgr == NULL )
+ return FALSE;
+
+ dev_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+ dev_hdr->type = NTDEV_TYPE_CLIENT_DEV;
+ dev_hdr->dispatch = gendrv_dispatch;
+ dev_hdr->start_io = gendrv_startio;
+ return TRUE;
+}
+
+PDEVICE_OBJECT
+gendrv_create_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER gen_drvr,
+DEV_HANDLE dev_handle
+)
+{
+ BOOL is_if;
+ PDEVICE_OBJECT pdev;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ ULONG dev_id;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ CHAR dev_name[ 64 ];
+ STRING string;
+ UNICODE_STRING name_string, symb_link;
+ NTSTATUS status;
+
+ if( dev_mgr == NULL || gen_drvr == NULL || dev_handle == 0 )
+ return NULL;
+
+ is_if = ( gen_drvr->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE ) ? 1 : 0;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_create_device(): entering...\n" ) );
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )gen_drvr->driver_ext;
+ dev_id = ( UCHAR )dev_id_from_handle( dev_handle );
+
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\Device\\gendev_%d", ( int )dev_id );
+ else
+ sprintf( dev_name, "\\Device\\genifdev_%d", ( int )dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &name_string, &string, TRUE );
+ pdev = NULL;
+
+ status = IoCreateDevice(
+ dev_mgr->usb_driver_obj,
+ sizeof( GENDRV_DEVICE_EXTENSION ),
+ &name_string,
+ FILE_USB_DEV_TYPE,
+ 0,
+ TRUE,
+ &pdev);
+
+ if( status == STATUS_SUCCESS )
+ {
+ //
+ // We do direct io
+ //
+ pdev->Flags |= DO_DIRECT_IO;
+
+ pdev->Flags &= ~DO_DEVICE_INITIALIZING;
+ pdev->StackSize = 2; //one for fdo, one for file device obj
+
+ pdev_ext = ( PGENDRV_DEVICE_EXTENSION )pdev->DeviceExtension;
+
+ //may be accessed by other thread
+
+ gendrv_init_dev_ext_hdr( pdev, dev_mgr );
+
+ pdev_ext->dev_id = ( UCHAR )dev_id;
+ pdev_ext->pdo = pdev;
+ pdev_ext->dev_handle = dev_handle;
+ pdev_ext->dev_mgr = dev_mgr;
+ pdev_ext->pdriver = gen_drvr;
+
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\DosDevices\\gendev%d", ( int )dev_id );
+ else
+ sprintf( dev_name, "\\DosDevices\\genifdev%d", ( int )dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoCreateSymbolicLink( &symb_link, &name_string );
+ RtlFreeUnicodeString( &symb_link );
+ KeInitializeEvent( &pdev_ext->sync_event, SynchronizationEvent, FALSE );
+ KeInitializeSpinLock( &pdev_ext->dev_lock );
+
+ }
+ RtlFreeUnicodeString( &name_string );
+ return pdev;
+}
+
+
+
+VOID
+gendrv_deferred_delete_device(
+PVOID context
+)
+{
+ PDEVICE_OBJECT dev_obj;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+ LARGE_INTEGER interval;
+
+ if( context == NULL )
+ return;
+
+ dev_obj = ( PDEVICE_OBJECT )context;
+ pdev_ext = dev_obj->DeviceExtension;
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext;
+
+ interval.QuadPart = -20000; //two ms
+
+ for( ; ; )
+ {
+ if( dev_obj->ReferenceCount )
+ KeDelayExecutionThread( KernelMode, TRUE, &interval );
+ else
+ {
+ KeDelayExecutionThread( KernelMode, TRUE, &interval );
+ if( dev_obj->ReferenceCount == 0 )
+ break;
+ }
+ }
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_deferred_delete_device(): delete device, 0x%x\n", dev_obj ) );
+
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ RemoveEntryList( &pdev_ext->dev_obj_link );
+ pdev_ext->ext_drvr_entry->ref_count--;
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ IoDeleteDevice( dev_obj );
+ return;
+}
+
+BOOL
+gendrv_delete_device(
+PUSB_DEV_MANAGER dev_mgr,
+PDEVICE_OBJECT dev_obj
+)
+{
+ BOOL is_if;
+ PUSB_DRIVER pdrvr;
+ PGENDRV_DEVICE_EXTENSION pdev_ext;
+ UCHAR dev_name[ 64 ];
+ STRING string;
+ UNICODE_STRING symb_link;
+ NTSTATUS status;
+ PGENDRV_DRVR_EXTENSION pdrvr_ext;
+
+ if( dev_mgr == NULL || dev_obj == 0 )
+ return FALSE;
+
+ pdev_ext = ( PGENDRV_DEVICE_EXTENSION )dev_obj->DeviceExtension;
+ pdrvr = pdev_ext->pdriver;
+ pdrvr_ext = ( PGENDRV_DRVR_EXTENSION )pdrvr->driver_ext;
+ is_if = if_dev( dev_obj );
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\DosDevices\\gendev%d", ( int )pdev_ext->dev_id );
+ else
+ sprintf( dev_name, "\\DosDevices\\genifdev%d", ( int )pdev_ext->dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoDeleteSymbolicLink( &symb_link );
+ RtlFreeUnicodeString( &symb_link );
+
+ if( pdev_ext->desc_buf )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "gendrv_delete_device(): delete desc_buf\n" ) );
+ usb_free_mem( pdev_ext->desc_buf );
+ pdev_ext->desc_buf = NULL;
+
+ }
+
+ if( dev_obj->ReferenceCount == 0 )
+ {
+ ExAcquireFastMutex( &pdrvr_ext->drvr_ext_mutex );
+ RemoveEntryList( &pdev_ext->dev_obj_link );
+ pdev_ext->ext_drvr_entry->ref_count--; //the ext_drvr_entry is actually in pdrvr_ext, so lock it.
+ ExReleaseFastMutex( &pdrvr_ext->drvr_ext_mutex );
+
+ IoDeleteDevice( dev_obj );
+ return TRUE;
+ }
+
+ // borrow from umss's work routine
+ return umss_schedule_workitem( dev_obj, gendrv_deferred_delete_device, NULL, 0 );
+}
+
+// must have the drvr_ext_mutex acquired.
+PGENDRV_EXT_DRVR_ENTRY
+gendrv_alloc_ext_drvr_entry(
+PGENDRV_DRVR_EXTENSION pdrvr_ext
+)
+{
+ LONG i;
+ if( pdrvr_ext == NULL )
+ return NULL;
+ if( pdrvr_ext->ext_drvr_count == GENDRV_MAX_EXT_DRVR )
+ return NULL;
+ for( i = 0; i < GENDRV_MAX_EXT_DRVR; i++ )
+ {
+ if( pdrvr_ext->ext_drvr_array[ i ].drvr_link.Flink == NULL &&
+ pdrvr_ext->ext_drvr_array[ i ].drvr_link.Blink == NULL )
+ {
+ return &pdrvr_ext->ext_drvr_array[ i ];
+ }
+ }
+ return NULL;
+}
+
+// must have the drvr_ext_mutex acquired.
+VOID
+gendrv_release_ext_drvr_entry(
+PGENDRV_DRVR_EXTENSION pdrvr_ext,
+PGENDRV_EXT_DRVR_ENTRY pentry
+)
+{
+ if( pdrvr_ext == NULL || pentry == NULL )
+ return;
+ RtlZeroMemory( pentry, sizeof( GENDRV_EXT_DRVR_ENTRY ) );
+ InitializeListHead( &pentry->dev_list );
+ return;
+}
+
+NTSTATUS
+gendrv_open_reg_key(
+OUT PHANDLE handle,
+IN HANDLE base_handle OPTIONAL,
+IN PUNICODE_STRING keyname,
+IN ACCESS_MASK desired_access,
+IN BOOLEAN create
+)
+
+/*++
+
+Routine Description:
+
+ Opens or creates a VOLATILE registry key using the name passed in based
+ at the BaseHandle node.
+
+Arguments:
+
+ Handle - Pointer to the handle which will contain the registry key that
+ was opened.
+
+ BaseHandle - Handle to the base path from which the key must be opened.
+
+ KeyName - Name of the Key that must be opened/created.
+
+ DesiredAccess - Specifies the desired access that the caller needs to
+ the key.
+
+ Create - Determines if the key is to be created if it does not exist.
+
+Return Value:
+
+ The function value is the final status of the operation.
+
+--*/
+
+{
+ OBJECT_ATTRIBUTES object_attr;
+ ULONG disposition;
+
+ //
+ // Initialize the object for the key.
+ //
+
+ InitializeObjectAttributes( &object_attr,
+ keyname,
+ OBJ_CASE_INSENSITIVE,
+ base_handle,
+ (PSECURITY_DESCRIPTOR) NULL );
+
+ //
+ // Create the key or open it, as appropriate based on the caller's
+ // wishes.
+ //
+
+ if (create)
+ {
+ return ZwCreateKey( handle,
+ desired_access,
+ &object_attr,
+ 0,
+ (PUNICODE_STRING) NULL,
+ REG_OPTION_VOLATILE,
+ &disposition );
+ }
+ else
+ {
+ return ZwOpenKey( handle,
+ desired_access,
+ &object_attr );
+ }
+ return STATUS_INVALID_PARAMETER;
+}
+
+NTSTATUS
+gendrv_get_key_value(
+IN HANDLE KeyHandle,
+IN PWSTR ValueName,
+OUT PKEY_VALUE_FULL_INFORMATION *Information
+)
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to retrieve the data for a registry key's value.
+ This is done by querying the value of the key with a zero-length buffer
+ to determine the size of the value, and then allocating a buffer and
+ actually querying the value into the buffer.
+
+ It is the responsibility of the caller to free the buffer.
+
+Arguments:
+
+ KeyHandle - Supplies the key handle whose value is to be queried
+
+ ValueName - Supplies the null-terminated Unicode name of the value.
+
+ Information - Returns a pointer to the allocated data buffer.
+
+Return Value:
+
+ The function value is the final status of the query operation.
+
+--*/
+
+{
+ UNICODE_STRING unicodeString;
+ NTSTATUS status;
+ PKEY_VALUE_FULL_INFORMATION infoBuffer;
+ ULONG keyValueLength;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &unicodeString, ValueName );
+
+ //
+ // Figure out how big the data value is so that a buffer of the
+ // appropriate size can be allocated.
+ //
+
+ status = ZwQueryValueKey( KeyHandle,
+ &unicodeString,
+ KeyValueFullInformation,
+ (PVOID) NULL,
+ 0,
+ &keyValueLength );
+ if (status != STATUS_BUFFER_OVERFLOW &&
+ status != STATUS_BUFFER_TOO_SMALL) {
+ return status;
+ }
+
+ //
+ // Allocate a buffer large enough to contain the entire key data value.
+ //
+
+ infoBuffer = usb_alloc_mem( NonPagedPool, keyValueLength );
+ if (!infoBuffer) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Query the data for the key value.
+ //
+
+ status = ZwQueryValueKey( KeyHandle,
+ &unicodeString,
+ KeyValueFullInformation,
+ infoBuffer,
+ keyValueLength,
+ &keyValueLength );
+ if (!NT_SUCCESS( status )) {
+ usb_free_mem( infoBuffer );
+ return status;
+ }
+
+ //
+ // Everything worked, so simply return the address of the allocated
+ // buffer to the caller, who is now responsible for freeing it.
+ //
+
+ *Information = infoBuffer;
+ return STATUS_SUCCESS;
+}
+
+VOID
+gendrv_startio(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ PIO_STACK_LOCATION irp_stack;
+ ULONG ctrl_code;
+ PUSB_DEV_MANAGER dev_mgr;
+ USE_IRQL;
+
+ if( dev_obj == NULL || irp == NULL )
+ return;
+
+ // standard process from walter oney
+ IoAcquireCancelSpinLock(&old_irql);
+ if (irp != dev_obj->CurrentIrp || irp->Cancel)
+ {
+ // already move on to other irp
+ IoReleaseCancelSpinLock( old_irql );
+ return;
+ }
+ else
+ {
+ IoSetCancelRoutine( irp, NULL );
+ }
+ IoReleaseCancelSpinLock( old_irql );
+
+ irp->IoStatus.Information = 0;
+
+ irp_stack = IoGetCurrentIrpStackLocation( irp );
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+ dev_mgr = ( ( PDEVEXT_HEADER )dev_obj->DeviceExtension )->dev_mgr;
+
+ if( irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL &&
+ irp_stack->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL )
+ {
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp );
+ }
+
+ switch( ctrl_code )
+ {
+ case IOCTL_SUBMIT_URB_RD:
+ case IOCTL_SUBMIT_URB_NOIO:
+ case IOCTL_SUBMIT_URB_WR:
+ {
+ LONG buf_size;
+ PURB purb;
+ KIRQL old_irql;
+ ULONG endp_idx, if_idx, user_buffer_length;
+ PUCHAR user_buffer;
+ PUSB_DEV pdev;
+ DEV_HANDLE endp_handle;
+ PUSB_ENDPOINT pendp;
+
+ NTSTATUS status;
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) )
+ {
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ purb = ( PURB )irp->AssociatedIrp.SystemBuffer;
+ if( ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR )
+ {
+ if( irp_stack->MajorFunction == IRP_MJ_DEVICE_CONTROL )
+ {
+ user_buffer = MmGetSystemAddressForMdl( irp->MdlAddress );
+ user_buffer_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
+ if( user_buffer_length == 0 )
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+ else
+ {
+ if( purb->data_buffer == NULL || purb->data_length == 0 )
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ user_buffer = purb->data_buffer;
+ user_buffer_length = purb->data_length;
+ }
+ }
+
+ purb->reference = 0;
+ endp_handle = purb->endp_handle;
+
+ if( usb_query_and_lock_dev( dev_mgr, endp_handle & ~0xffff, &pdev ) != STATUS_SUCCESS )
+ {
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_IO_DEVICE_ERROR, irp );
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB \
+ || ( dev_state( pdev ) < USB_DEV_STATE_ADDRESSED ) )
+
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ goto ERROR_OUT1;
+ }
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ADDRESSED
+ && !default_endp_handle( endp_handle ) )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto ERROR_OUT1;
+ }
+
+ if_idx = if_idx_from_handle( endp_handle );
+ endp_idx = endp_idx_from_handle( endp_handle );
+
+ //if_idx exceeds the upper limit
+ if( pdev->usb_config )
+ {
+ if( if_idx >= pdev->usb_config->if_count
+ || endp_idx >= pdev->usb_config->interf[ if_idx ].endp_count )
+ {
+ if( !default_endp_handle( endp_handle ) )
+ {
+ status = STATUS_INVALID_DEVICE_STATE;
+ goto ERROR_OUT1;
+ }
+ }
+ }
+
+ endp_from_handle( pdev, endp_handle, pendp );
+
+ // FIXME: don't know what evil will let loose
+ if( endp_type( pendp ) != USB_ENDPOINT_XFER_CONTROL )
+ {
+ if( user_buffer_length > 16 )
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto ERROR_OUT1;
+ }
+ }
+
+ purb->pirp = irp;
+ purb->context = dev_mgr;
+ purb->reference = ctrl_code;
+
+ if( ctrl_code == IOCTL_SUBMIT_URB_RD || ctrl_code == IOCTL_SUBMIT_URB_WR )
+ {
+ purb->data_buffer = user_buffer;
+ purb->data_length = user_buffer_length;
+ purb->completion = disp_urb_completion;
+ }
+ else
+ {
+ purb->completion = disp_noio_urb_completion;
+ }
+
+ unlock_dev( pdev, FALSE );
+
+ //
+ // we have to register irp before the urb is scheduled to
+ // avoid race condition.
+ //
+ ASSERT( dev_mgr_register_irp( dev_mgr, irp, purb ) );
+ //
+ // the irp can not be canceled at this point, since it is
+ // now the current irp and not in any urb queue. dev_mgr_cancel_irp
+ // can not find it and simply return.
+ //
+ // FIXME: there is a time window that the irp is registered and
+ // the urb is not queued. In the meantime, the cancel
+ // request may come and cause the irp removed from the irp
+ // queue while fail to cancel due to urb not in any urb queue .
+ // Thus from that point on, the irp can not be canceled till it
+ // is completed or hanging there forever.
+ //
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ // unmark the pending bit
+ IoGetCurrentIrpStackLocation( ( irp ) )->Control &= ~SL_PENDING_RETURNED;
+ dev_mgr_remove_irp( dev_mgr, irp );
+ }
+ usb_unlock_dev( pdev );
+ if( status != STATUS_PENDING)
+ {
+ irp->IoStatus.Status = status;
+ GENDRV_COMPLETE_START_IO( dev_obj, status, irp );
+ }
+ return;
+
+ERROR_OUT1:
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ irp->IoStatus.Information = 0;
+ GENDRV_COMPLETE_START_IO( dev_obj, status, irp );
+ }
+ }
+ GENDRV_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp );
+}
+
+VOID
+gendrv_clean_up_queued_irps(
+PDEVICE_OBJECT dev_obj
+)
+{
+ // called when device may not function or about to be removed, need cleanup
+ KIRQL cancelIrql;
+ PIRP irp, cur_irp;
+ PKDEVICE_QUEUE_ENTRY packet;
+ LIST_ENTRY cancel_irps, *pthis, *pnext;
+ PDEVEXT_HEADER dev_hdr;
+
+ //
+ // cancel all the irps in the queue
+ //
+ if( dev_obj == NULL )
+ return;
+
+ InitializeListHead( &cancel_irps );
+
+ // remove the irps from device queue
+ IoAcquireCancelSpinLock( &cancelIrql );
+ cur_irp = dev_obj->CurrentIrp;
+ while( packet = KeRemoveDeviceQueue( &dev_obj->DeviceQueue ) )
+ {
+ irp = struct_ptr( packet, IRP, Tail.Overlay.DeviceQueueEntry );
+ InsertTailList( &cancel_irps, &irp->Tail.Overlay.DeviceQueueEntry.DeviceListEntry );
+ }
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ // cancel the irps in process
+ // we did not cancel the current irp, it will be done by hcd when
+ // disconnect is detected.
+ // remove_irp_from_list( &dev_mgr->irp_list, cur_irp, dev_mgr );
+
+ while( IsListEmpty( &cancel_irps ) == FALSE )
+ {
+ pthis = RemoveHeadList( &cancel_irps );
+ irp = struct_ptr( pthis, IRP, Tail.Overlay.DeviceQueueEntry.DeviceListEntry );
+ irp->IoStatus.Information = 0;
+ irp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+ }
+ return;
+}
+
+VOID
+gendrv_cancel_queued_irp(
+PDEVICE_OBJECT dev_obj,
+PIRP pirp
+)
+{
+ // cancel routine for irps queued in the device queue
+ PUSB_DEV_MANAGER dev_mgr;
+ PDEVEXT_HEADER pdev_ext_hdr;
+ ULONG i;
+
+ pdev_ext_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+ dev_mgr = pdev_ext_hdr->dev_mgr;
+
+ if( dev_obj->CurrentIrp == pirp )
+ {
+ // just before start_io set the cancel routine to null
+ IoReleaseCancelSpinLock( pirp->CancelIrql );
+ // we did not IoStartNextPacket, leave it for dev_mgr_cancel_irp, that
+ // is user have to call CancelIo again.
+ return;
+ }
+
+ KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry );
+ IoReleaseCancelSpinLock( pirp->CancelIrql );
+
+ pirp->IoStatus.Information = 0;
+ pirp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest( pirp, IO_NO_INCREMENT );
+ // the device queue is moved on, no need to call IoStartNextPacket
+ return;
+}
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h
new file mode 100644
index 00000000000..1ef8039a8df
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/gendrv.h
@@ -0,0 +1,65 @@
+#ifndef __GENDRV_H__
+#define __GENDRV_H__
+
+#include "td.h"
+#include "hub.h"
+
+#define GENDRV_MAX_EXT_DRVR 2
+#define GENDRV_DRVR_FLAG_IF_DRVR 0x01
+
+#define GENDRV_MSG_ADDDEVICE 0x01
+#define GENDRV_MSG_STOPDEVICE 0x02
+#define GENDRV_MSG_DISCDEVICE 0x03
+
+typedef struct _GENDRV_EXT_DRVR_ENTRY
+{
+ LIST_ENTRY drvr_link; // used for dynamic load/unload driver
+ ULONG drvr_key; // used to find the driver loaded, the key
+ // is ( vendor_id << 16 )|( product_id ) or
+ // ( class_id << 16 ) | ( sub_class_id << 8 ) | ( protocol_id ))
+ PDRIVER_OBJECT pext_drvr; // the external driver count
+ ULONG ref_count; // number of devices attached
+ LIST_HEAD dev_list; // link the devices by deviceExtension->dev_obj_link
+
+} GENDRV_EXT_DRVR_ENTRY, *PGENDRV_EXT_DRVR_ENTRY;
+
+typedef struct _GENDRV_DRVR_EXTENSION
+{
+ FAST_MUTEX drvr_ext_mutex;
+ ULONG ext_drvr_count; // loaded driver count
+ LIST_HEAD ext_drvr_list;
+ GENDRV_EXT_DRVR_ENTRY ext_drvr_array[ GENDRV_MAX_EXT_DRVR ];
+
+}GENDRV_DRVR_EXTENSION, *PGENDRV_DRVR_EXTENSION;
+
+typedef struct _GENDRV_IF_CTX
+{
+ UCHAR if_idx; // valid for if device
+ PUSB_INTERFACE_DESC pif_desc;
+
+} GENDRV_IF_CTX, *PGENDRV_IF_IDX;
+
+typedef struct _GENDRV_DEVICE_EXTENSION
+{
+ //this structure is the device extension for dev_obj
+ //created for the device.
+ DEVEXT_HEADER dev_ext_hdr;
+ ULONG flags;
+ LIST_ENTRY dev_obj_link; // this link is used by the driver object to track the existing dev_objs
+ PGENDRV_EXT_DRVR_ENTRY ext_drvr_entry;
+
+ PDEVICE_OBJECT pdo; // Our device object
+ DEV_HANDLE dev_handle; // handle to the usb_dev under
+ PUCHAR desc_buf;
+ UCHAR dev_id; // used to build symbolic link
+ GENDRV_IF_CTX if_ctx;
+
+ KEVENT sync_event;
+ KSPIN_LOCK dev_lock;
+
+ PUSB_DRIVER pdriver;
+ PUSB_DEV_MANAGER dev_mgr;
+
+} GENDRV_DEVICE_EXTENSION, *PGENDRV_DEVICE_EXTENSION;
+
+#endif
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hcd.h b/reactos/drivers/usb/nt4compat/usbdriver/hcd.h
new file mode 100644
index 00000000000..102bb693688
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/hcd.h
@@ -0,0 +1,71 @@
+#ifndef __HCD_H__
+#define __HCD_H__
+
+#include "ntddk.h"
+
+#define HCD_TYPE_MASK 0xf0
+#define HCD_TYPE_UHCI 0x10
+#define HCD_TYPE_OHCI 0x20
+#define HCD_TYPE_EHCI 0x30
+
+#define hcd_type( hCD ) ( ( ( hCD )->flags ) & HCD_TYPE_MASK )
+#define usb2( hCD ) ( hcd_type( hCD ) == HCD_TYPE_EHCI )
+
+#define HCD_ID_MASK 0xf
+
+#ifndef BOOL
+#define BOOL ULONG
+#endif
+
+#define HCD_DISP_READ_PORT_COUNT 1 // the param is a pointer to UCHAR
+#define HCD_DISP_READ_RH_DEV_CHANGE 2 // the param is a buffer to hold conn change on all the port
+ // must have the rh dev_lock acquired
+
+typedef VOID ( *PHCD_SET_DEV_MGR )( struct _HCD* hcd, struct _USB_DEV_MANAGER *dev_mgr );
+typedef struct _USB_DEV_MANAGER* ( *PHCD_GET_DEV_MGR )( struct _HCD* hcd );
+typedef ULONG ( *PHCD_GET_TYPE )( struct _HCD* hcd );
+typedef VOID ( *PHCD_SET_ID )( struct _HCD* hcd, UCHAR id );
+typedef UCHAR ( *PHCD_GET_ID )( struct _HCD* hcd );
+typedef UCHAR ( *PHCD_ALLOC_ADDR )( struct _HCD* hcd );
+typedef VOID ( *PHCD_FREE_ADDR )( struct _HCD* hcd, UCHAR addr );
+typedef NTSTATUS ( *PHCD_SUBMIT_URB )( struct _HCD* hcd, struct _USB_DEV *pdev, struct _USB_ENDPOINT *pendp, struct _URB *purb );
+typedef VOID ( *PHCD_GENERIC_URB_COMPLETION )( struct _URB *purb, PVOID context ); //we can get te hcd from purb
+typedef struct _USB_DEV* ( *PHCD_GET_ROOT_HUB )( struct _HCD* hcd );
+typedef VOID ( *PHCD_SET_ROOT_HUB )( struct _HCD* hcd, struct _USB_DEV *root_hub );
+typedef BOOL ( *PHCD_REMOVE_DEVICE )( struct _HCD* hcd, struct _USB_DEV *pdev );
+typedef BOOL ( *PHCD_RH_RESET_PORT )( struct _HCD* hcd, UCHAR port_idx ); //must have the rh dev_lock acquired
+typedef BOOL ( *PHCD_RELEASE )( struct _HCD* hcd ); //must have the rh dev_lock acquired
+typedef NTSTATUS( *PHCD_CANCEL_URB)( struct _HCD* hcd, struct _USB_DEV *pdev, struct _USB_ENDPOINT* pendp, struct _URB *purb );
+typedef BOOL ( *PHCD_START )( struct _HCD* hcd ); //must have the rh dev_lock acquired
+typedef NTSTATUS ( *PHCD_DISPATCH )( struct _HCD* hcd, LONG disp_code, PVOID param ); // locking depends on type of code
+
+typedef struct _HCD
+{
+ PHCD_SET_DEV_MGR hcd_set_dev_mgr;
+ PHCD_GET_DEV_MGR hcd_get_dev_mgr;
+ PHCD_GET_TYPE hcd_get_type;
+ PHCD_SET_ID hcd_set_id;
+ PHCD_GET_ID hcd_get_id;
+ PHCD_ALLOC_ADDR hcd_alloc_addr;
+ PHCD_FREE_ADDR hcd_free_addr;
+ PHCD_SUBMIT_URB hcd_submit_urb;
+ PHCD_GENERIC_URB_COMPLETION hcd_generic_urb_completion;
+ PHCD_GET_ROOT_HUB hcd_get_root_hub;
+ PHCD_SET_ROOT_HUB hcd_set_root_hub;
+ PHCD_REMOVE_DEVICE hcd_remove_device;
+ PHCD_RH_RESET_PORT hcd_rh_reset_port;
+ PHCD_RELEASE hcd_release;
+ PHCD_CANCEL_URB hcd_cancel_urb;
+ PHCD_START hcd_start;
+ PHCD_DISPATCH hcd_dispatch;
+
+ //interfaces for all the host controller
+ ULONG flags; //hcd types | hcd id
+ ULONG conn_count; //statics for connection activities
+ struct _USB_DEV_MANAGER *dev_mgr; //pointer manager
+ UCHAR dev_addr_map[ 128 / 8 ]; //bitmap for the device addrs
+ struct _DEVICE_EXTENSION *pdev_ext;
+
+} HCD, *PHCD;
+
+#endif
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hub.c b/reactos/drivers/usb/nt4compat/usbdriver/hub.c
new file mode 100644
index 00000000000..a01608e64c2
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/hub.c
@@ -0,0 +1,5184 @@
+/**
+ * hub.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "hub.h"
+#include "td.h"
+#include "debug.h"
+#include "umss.h"
+//----------------------------------------------------------
+//event pool routines
+#define crash_machine() \
+{ ( ( PUSB_DEV ) 0 )->flags = 0x12345; }
+
+#define hub_if_from_dev( pdEV, pIF ) \
+{\
+ int i;\
+ for( i = 0; i < pdEV->usb_config->if_count; i ++ )\
+ {\
+ if( pdEV->usb_config->interf[ i ].pusb_if_desc->bInterfaceClass\
+ == USB_CLASS_HUB )\
+ {\
+ break;\
+ }\
+ }\
+\
+ if( i < pdEV->usb_config->if_count )\
+ pIF = &pdev->usb_config->interf[ i ];\
+ else\
+ pIF = NULL;\
+\
+}
+
+#define hub_ext_from_dev( pdEV ) ( ( PHUB2_EXTENSION )pdEV->dev_ext )
+
+#define realloc_buf( pdEV, puRB ) \
+{\
+ PBYTE data_buf;\
+ int i;\
+ data_buf = usb_alloc_mem( NonPagedPool, ( pdEV )->desc_buf_size += 1024 );\
+ RtlZeroMemory( data_buf, ( pdEV )->desc_buf_size );\
+ for( i = 0; i < ( LONG )( puRB )->context; i++ )\
+ {\
+ data_buf[ i ] = ( pdEV )->desc_buf[ i ];\
+ }\
+ usb_free_mem( ( pdEV )->desc_buf );\
+ ( pdEV )->desc_buf = data_buf;\
+ ( pdEV )->pusb_dev_desc = ( PUSB_DEVICE_DESC )( pdEV )->desc_buf;\
+ ( puRB )->data_buffer = &data_buf[ ( LONG ) ( puRB )->context ];\
+}
+
+
+BOOL
+dev_mgr_set_if_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle,
+PUSB_DRIVER pdriver,
+PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle, and must have dev_lock acquired.
+)
+{
+ ULONG i;
+ USE_IRQL;
+
+ if( dev_mgr == NULL || if_handle == 0 || pdriver == NULL )
+ return FALSE;
+
+ i = if_idx_from_handle( if_handle );
+ if( pdev != NULL )
+ {
+ if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB )
+ {
+ pdev->usb_config->interf[ i ].pif_drv = pdriver;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if( usb_query_and_lock_dev( dev_mgr, if_handle, &pdev ) != STATUS_SUCCESS )
+ return FALSE;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) != USB_DEV_STATE_ZOMB )
+ {
+ pdev->usb_config->interf[ i ].pif_drv = pdriver;
+ }
+ unlock_dev( pdev, TRUE );
+ usb_unlock_dev( pdev );
+ return TRUE;
+}
+
+BOOL
+dev_mgr_set_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle,
+PUSB_DRIVER pdriver,
+PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle
+)
+{
+ USE_IRQL;
+
+ if( dev_mgr == NULL || dev_handle == 0 || pdriver == NULL )
+ return FALSE;
+
+ if( pdev != NULL )
+ {
+ if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB )
+ {
+ pdev->dev_driver = pdriver;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS )
+ return FALSE;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) < USB_DEV_STATE_BEFORE_ZOMB )
+ {
+ pdev->dev_driver = pdriver;
+ }
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+
+ return TRUE;
+
+}
+
+USB_DRIVER g_driver_list[ DEVMGR_MAX_DRIVERS ];
+USB_DEV_MANAGER g_dev_mgr;
+extern ULONG cpu_clock_freq;
+
+BOOL
+hub_check_reset_port_status(
+PUSB_DEV pdev,
+LONG port_idx
+);
+
+BOOL
+hub_start_next_reset_port(
+PUSB_DEV_MANAGER dev_mgr,
+BOOL from_dpc
+);
+
+VOID
+hub_reexamine_port_status_queue(
+PUSB_DEV hub_dev,
+ULONG port_idx,
+BOOL from_dpc
+);
+
+void
+hub_int_completion(
+PURB purb,
+PVOID pcontext
+);
+
+VOID
+hub_get_port_status_completion(
+PURB purb,
+PVOID context
+);
+
+VOID
+hub_clear_port_feature_completion(
+PURB purb,
+PVOID context
+);
+
+VOID
+hub_event_examine_status_que(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context, //hub_ext
+ULONG param //port_idx
+);
+
+VOID
+hub_timer_wait_dev_stable(
+PUSB_DEV pdev,
+PVOID context //port-index
+);
+
+VOID
+hub_event_dev_stable(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context, //hub_ext
+ULONG param //port_idx
+);
+
+VOID
+hub_post_esq_event(
+PUSB_DEV pdev,
+BYTE port_idx,
+PROCESS_EVENT pe
+);
+
+void
+hub_set_cfg_completion(
+PURB purb,
+PVOID pcontext
+);
+
+void
+hub_get_hub_desc_completion(
+PURB purb,
+PVOID pcontext
+);
+
+NTSTATUS
+hub_start_int_request(
+PUSB_DEV pdev
+);
+
+BOOL
+hub_remove_reset_event(
+PUSB_DEV pdev,
+ULONG port_idx,
+BOOL from_dpc
+);
+
+BOOL
+hub_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+hub_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+hub_connect(
+PCONNECT_DATA init_param,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+hub_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+hub_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+NTSTATUS
+hub_disable_port_request(
+PUSB_DEV pdev,
+UCHAR port_idx
+);
+
+VOID
+hub_start_reset_port_completion(
+PURB purb,
+PVOID context
+);
+
+BOOL
+dev_mgr_start_config_dev(
+PUSB_DEV pdev
+);
+
+BOOL
+dev_mgr_event_init(
+PUSB_DEV dev, //always null. we do not use this param
+ULONG event,
+ULONG context,
+ULONG param
+);
+
+VOID
+dev_mgr_get_desc_completion(
+PURB purb,
+PVOID context
+);
+
+VOID
+dev_mgr_event_select_driver(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context,
+ULONG param
+);
+
+LONG
+dev_mgr_score_driver_for_dev(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver,
+PUSB_DEVICE_DESC pdev_desc
+);
+
+NTSTATUS
+dev_mgr_destroy_usb_config(
+PUSB_CONFIGURATION pcfg
+);
+
+BOOL
+dev_mgr_start_select_driver(
+PUSB_DEV pdev
+);
+
+VOID
+dev_mgr_cancel_irp(
+PDEVICE_OBJECT pdev_obj,
+PIRP pirp
+);
+
+BOOL
+init_event_pool(
+PUSB_EVENT_POOL pool
+)
+{
+ int i;
+
+ if( pool == NULL )
+ return FALSE;
+
+ if( ( pool->event_array = usb_alloc_mem(
+ NonPagedPool,
+ sizeof( USB_EVENT ) * MAX_EVENTS ) )
+ == NULL)
+ return FALSE;
+
+ InitializeListHead( &pool->free_que );
+ KeInitializeSpinLock( &pool->pool_lock );
+ pool->total_count = MAX_EVENTS;
+ pool->free_count = 0;
+
+ for( i = 0; i < MAX_EVENTS; i++ )
+ {
+ free_event( pool, &pool->event_array[ i ] );
+ }
+
+ return TRUE;
+}
+
+BOOL
+free_event(
+PUSB_EVENT_POOL pool,
+PUSB_EVENT pevent
+)
+{
+ if( pool == NULL || pevent == NULL )
+ {
+ return FALSE;
+ }
+
+ RtlZeroMemory( pevent, sizeof( USB_EVENT ) );
+ InsertTailList( &pool->free_que, (PLIST_ENTRY) pevent );
+ pool->free_count++;
+ usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "free_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, pevent ) );
+
+ return TRUE;
+}
+
+PUSB_EVENT
+alloc_event(
+PUSB_EVENT_POOL pool,
+LONG count
+) //null if failed
+{
+ PUSB_EVENT new;
+ if( pool == NULL || count != 1)
+ return NULL;
+
+ if( pool->free_count == 0 )
+ return NULL;
+
+ new = ( PUSB_EVENT )RemoveHeadList( &pool->free_que );
+ pool->free_count --;
+
+ usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "alloc_event(): alloced=0x%x, addr=0x%x\n", MAX_EVENTS - pool->free_count, new ) );
+ return new;
+}
+
+BOOL
+destroy_event_pool(
+PUSB_EVENT_POOL pool
+)
+{
+ if( pool == NULL )
+ return FALSE;
+
+ InitializeListHead( &pool->free_que );
+ pool->free_count = pool->total_count = 0;
+ usb_free_mem( pool->event_array );
+ pool->event_array = NULL;
+
+ return TRUE;
+}
+
+VOID
+event_list_default_process_event(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context,
+ULONG param
+)
+{}
+
+//----------------------------------------------------------
+//timer_svc pool routines
+
+BOOL
+init_timer_svc_pool(
+PTIMER_SVC_POOL pool
+)
+{
+ int i;
+ if( pool == NULL )
+ return FALSE;
+
+ pool->timer_svc_array = usb_alloc_mem( NonPagedPool, sizeof( TIMER_SVC ) * MAX_TIMER_SVCS );
+ InitializeListHead( &pool->free_que );
+ pool->free_count = 0;
+ pool->total_count = MAX_TIMER_SVCS;
+ KeInitializeSpinLock( &pool->pool_lock );
+
+ for( i = 0; i < MAX_TIMER_SVCS; i++ )
+ {
+ free_timer_svc( pool, &pool->timer_svc_array[ i ] );
+ }
+
+ return TRUE;
+}
+
+BOOL
+free_timer_svc(
+PTIMER_SVC_POOL pool,
+PTIMER_SVC ptimer
+)
+{
+ if( pool == NULL || ptimer == NULL )
+ return FALSE;
+
+ RtlZeroMemory( ptimer, sizeof( TIMER_SVC ) );
+ InsertTailList( &pool->free_que, ( PLIST_ENTRY )&ptimer->timer_svc_link );
+ pool->free_count ++;
+
+ return TRUE;
+}
+
+PTIMER_SVC
+alloc_timer_svc(
+PTIMER_SVC_POOL pool,
+LONG count
+) //null if failed
+{
+ PTIMER_SVC new;
+ if( pool == NULL || count != 1 )
+ return NULL;
+
+ if( pool->free_count <= 0 )
+ return NULL;
+
+ new = ( PTIMER_SVC )RemoveHeadList( &pool->free_que );
+ pool->free_count --;
+ return new;
+
+}
+
+BOOL
+destroy_timer_svc_pool(
+PTIMER_SVC_POOL pool
+)
+{
+ if( pool == NULL )
+ return FALSE;
+
+ usb_free_mem( pool->timer_svc_array );
+ pool->timer_svc_array = NULL;
+ InitializeListHead( &pool->free_que );
+ pool->free_count = 0;
+ pool->total_count = 0;
+
+ return TRUE;
+}
+
+BOOL
+dev_mgr_post_event(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_EVENT event
+)
+{
+ KIRQL old_irql;
+
+ if( dev_mgr == NULL || event == NULL )
+ return FALSE;
+
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+ InsertTailList( &dev_mgr->event_list, &event->event_link );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ return TRUE;
+}
+
+
+VOID
+event_list_default_process_queue(
+PLIST_HEAD event_list,
+PUSB_EVENT_POOL event_pool,
+PUSB_EVENT usb_event,
+PUSB_EVENT out_event
+)
+{
+ //remove the first event from the event list, and copy it to
+ //out_event
+
+ if( event_list == NULL || event_pool == NULL || usb_event == NULL || out_event == NULL )
+ return;
+
+ RemoveEntryList( &usb_event->event_link );
+ RtlCopyMemory( out_event, usb_event, sizeof( USB_EVENT ) );
+ free_event( event_pool, usb_event );
+ return;
+}
+
+BOOL
+psq_enqueue(
+PPORT_STATUS_QUEUE psq,
+ULONG status
+)
+{
+ if( psq == NULL )
+ return FALSE;
+
+ if( psq_is_full( psq ) )
+ return FALSE;
+
+ psq->port_status[ psq->status_count ].wPortChange = HIWORD( status );
+ psq->port_status[ psq->status_count ].wPortStatus = LOWORD( status );
+
+ psq->status_count++;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("psq_enqueue(): last status=0x%x, status count=0x%x, port_flag=0x%x\n", \
+ status, \
+ psq->status_count, \
+ psq->port_flags ) );
+ return TRUE;
+
+}
+
+VOID
+psq_init(
+PPORT_STATUS_QUEUE psq
+)
+{
+ RtlZeroMemory( psq, sizeof( PORT_STATUS_QUEUE ) );
+ psq->port_flags = STATE_IDLE | USB_PORT_FLAG_DISABLE;
+}
+
+ULONG
+psq_outqueue(
+PPORT_STATUS_QUEUE psq
+) //return 0xffffffff if no element
+{
+ ULONG status;
+
+ if( psq == NULL )
+ return 0;
+
+ if( psq_is_empty( psq ) )
+ return 0;
+
+ status = ( ( PULONG )&psq->port_status )[ 0 ];
+ psq->port_status[ 0 ] = psq->port_status[ 1 ];
+ psq->port_status[ 1 ] = psq->port_status[ 2 ];
+ psq->port_status[ 2 ] = psq->port_status[ 3 ];
+ psq->status_count--;
+
+ return status;
+}
+
+BOOL
+psq_push(
+PPORT_STATUS_QUEUE psq,
+ULONG status
+)
+{
+ if( psq == NULL )
+ return FALSE;
+
+ status = ( ( PULONG )&psq->port_status )[ 0 ];
+ psq->port_status[ 3 ] = psq->port_status[ 2 ];
+ psq->port_status[ 2 ] = psq->port_status[ 1 ];
+ psq->port_status[ 1 ] = psq->port_status[ 0 ];
+
+ ( ( PULONG )&psq->port_status )[ 0 ]= status;
+
+ psq->status_count++;
+ psq->status_count = ( ( 4 > psq->status_count ) ? psq->status_count : 4 );
+
+ return TRUE;
+}
+
+VOID
+dev_mgr_driver_entry_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdrvr
+)
+{
+ // Device Info
+
+ RtlZeroMemory( pdrvr, sizeof( USB_DRIVER ) * DEVMGR_MAX_DRIVERS );
+
+ pdrvr[ RH_DRIVER_IDX ].driver_init = rh_driver_init; // in fact, this routine will init the rh device rather that the driver struct.
+ pdrvr[ RH_DRIVER_IDX ].driver_destroy = rh_driver_destroy; // we do not need rh to destroy currently, since that may means fatal hardware failure
+
+ pdrvr[ HUB_DRIVER_IDX ].driver_init = hub_driver_init; //no need, since dev_mgr is also a hub driver
+ pdrvr[ HUB_DRIVER_IDX ].driver_destroy = hub_driver_destroy;
+
+ pdrvr[ UMSS_DRIVER_IDX ].driver_init = umss_if_driver_init;
+ pdrvr[ UMSS_DRIVER_IDX ].driver_destroy = umss_if_driver_destroy;
+
+ pdrvr[ COMP_DRIVER_IDX ].driver_init = compdev_driver_init;
+ pdrvr[ COMP_DRIVER_IDX ].driver_destroy = compdev_driver_destroy;
+
+ pdrvr[ GEN_DRIVER_IDX ].driver_init = gendrv_driver_init;
+ pdrvr[ GEN_DRIVER_IDX ].driver_destroy = gendrv_driver_destroy;
+
+ pdrvr[ GEN_IF_DRIVER_IDX ].driver_init = gendrv_if_driver_init;
+ pdrvr[ GEN_IF_DRIVER_IDX ].driver_destroy = gendrv_if_driver_destroy;
+}
+
+BOOL
+dev_mgr_strobe(
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ PUSB_EVENT pevent;
+ HANDLE thread_handle;
+
+ if( dev_mgr == NULL )
+ return FALSE;
+ if( dev_mgr->hcd_count == 0 )
+ return FALSE;
+
+ dev_mgr->term_flag = FALSE;
+
+ if( dev_mgr->hcd_count == 0 )
+ return FALSE;
+
+ KeInitializeSpinLock( &dev_mgr->event_list_lock );
+ InitializeListHead( &dev_mgr->event_list );
+ init_event_pool( &dev_mgr->event_pool );
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ {
+ destroy_event_pool( &dev_mgr->event_pool );
+ return FALSE;
+ }
+
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->event = USB_EVENT_INIT_DEV_MGR;
+
+ pevent->process_queue = event_list_default_process_queue;
+ pevent->process_event = dev_mgr_event_init;
+
+ pevent->context = ( ULONG )dev_mgr;
+
+ KeInitializeEvent( &dev_mgr->wake_up_event,
+ SynchronizationEvent,
+ FALSE );
+
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+
+ if( PsCreateSystemThread(
+ &thread_handle,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ dev_mgr_thread,
+ dev_mgr )
+ != STATUS_SUCCESS )
+ {
+ destroy_event_pool( &dev_mgr->event_pool );
+ return FALSE;
+ }
+
+ ObReferenceObjectByHandle(
+ thread_handle,
+ THREAD_ALL_ACCESS,
+ NULL,
+ KernelMode,
+ (PVOID*) &dev_mgr->pthread,
+ NULL);
+
+ ZwClose( thread_handle );
+
+ return TRUE;
+}
+
+BOOL
+dev_mgr_event_init(
+PUSB_DEV pdev, //always null. we do not use this param
+ULONG event,
+ULONG context,
+ULONG param
+)
+{
+ LARGE_INTEGER due_time;
+ PUSB_DEV_MANAGER dev_mgr;
+ LONG i;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_event_init(): dev_mgr=0x%x, event=0x%x\n", context, event ) );
+ dev_mgr = ( PUSB_DEV_MANAGER ) context;
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ if( event != USB_EVENT_INIT_DEV_MGR )
+ return FALSE;
+
+ //dev_mgr->root_hub = NULL;
+ KeInitializeTimer( &dev_mgr->dev_mgr_timer );
+
+ KeInitializeDpc( &dev_mgr->dev_mgr_timer_dpc,
+ dev_mgr_timer_dpc_callback,
+ ( PVOID )dev_mgr );
+
+ KeInitializeSpinLock( &dev_mgr->timer_svc_list_lock );
+ InitializeListHead( &dev_mgr->timer_svc_list );
+ init_timer_svc_pool( &dev_mgr->timer_svc_pool );
+ dev_mgr->timer_click = 0;
+
+ init_irp_list( &dev_mgr->irp_list );
+
+ KeInitializeSpinLock( &dev_mgr->dev_list_lock );
+ InitializeListHead( &dev_mgr->dev_list );
+
+ dev_mgr->hub_count = 0;
+ InitializeListHead( &dev_mgr->hub_list );
+
+ dev_mgr->conn_count = 0;
+ dev_mgr->driver_list = g_driver_list;
+
+ dev_mgr_driver_entry_init( dev_mgr, dev_mgr->driver_list );
+
+ for( i = 0; i < DEVMGR_MAX_DRIVERS; i++ )
+ {
+ if( dev_mgr->driver_list[ i ].driver_init == NULL )
+ continue;
+
+ if( dev_mgr->driver_list[ i ].driver_init( dev_mgr, &dev_mgr->driver_list[ i ] ) == FALSE )
+ break;
+ }
+ if( i == DEVMGR_MAX_DRIVERS )
+ {
+ due_time.QuadPart = -( DEV_MGR_TIMER_INTERVAL_NS - 10 );
+
+ KeSetTimerEx( &dev_mgr->dev_mgr_timer,
+ due_time,
+ DEV_MGR_TIMER_INTERVAL_MS,
+ &dev_mgr->dev_mgr_timer_dpc );
+
+ return TRUE;
+ }
+
+ i--;
+
+ for( ; i >= 0; i-- )
+ {
+ if( dev_mgr->driver_list[ i ].driver_destroy )
+ dev_mgr->driver_list[ i ].driver_destroy( dev_mgr, &dev_mgr->driver_list[ i ] );
+ }
+
+ KeCancelTimer( &dev_mgr->dev_mgr_timer );
+ KeRemoveQueueDpc( &dev_mgr->dev_mgr_timer_dpc );
+ return FALSE;
+
+}
+
+VOID dev_mgr_destroy(
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ LONG i;
+ // oops...
+ KeCancelTimer ( &dev_mgr->dev_mgr_timer );
+ KeRemoveQueueDpc( &dev_mgr->dev_mgr_timer_dpc );
+
+ for( i = DEVMGR_MAX_DRIVERS - 1; i >= 0; i-- )
+ dev_mgr->driver_list[ i ].driver_destroy( dev_mgr, &dev_mgr->driver_list[ i ]);
+
+ destroy_irp_list( &dev_mgr->irp_list );
+ destroy_timer_svc_pool( &dev_mgr->timer_svc_pool );
+ destroy_event_pool( &dev_mgr->event_pool );
+
+}
+
+VOID
+dev_mgr_thread(
+PVOID context
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_EVENT pevent;
+ PLIST_ENTRY pthis, pnext;
+ USB_EVENT usb_event;
+ LARGE_INTEGER time_out;
+ NTSTATUS status;
+ BOOL dev_mgr_inited;
+ KIRQL old_irql;
+ LONG i;
+
+ dev_mgr = ( PUSB_DEV_MANAGER )context;
+ dev_mgr_inited = FALSE;
+ usb_cal_cpu_freq();
+ time_out.u.LowPart = ( 10 * 1000 * 1000 ) * 100 - 1; //1 minutes
+ time_out.u.HighPart = 0;
+ time_out.QuadPart = -time_out.QuadPart;
+
+ //usb_dbg_print( DBGLVL_MAXIMUM + 1, ( "dev_mgr_thread(): current uhci status=0x%x\n", uhci_status( dev_mgr->pdev_ext->uhci ) ) );
+
+ while( dev_mgr->term_flag == FALSE )
+ {
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+ if( IsListEmpty( &dev_mgr->event_list ) == TRUE )
+ {
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ status = KeWaitForSingleObject(
+ &dev_mgr->wake_up_event,
+ Executive,
+ KernelMode,
+ TRUE,
+ &time_out
+ );
+ continue;
+ }
+
+ // usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_thread(): current element in event list is 0x%x\n", \
+ // dbg_count_list( &dev_mgr->event_list ) ) );
+
+ dev_mgr_inited = TRUE; //since we have post one event, if this statement is executed, dev_mgr_event_init must be called sometime later or earlier
+
+ ListFirst( &dev_mgr->event_list, pthis );
+ pevent = struct_ptr( pthis, USB_EVENT, event_link );
+
+ while( pevent && ( ( pevent->flags & USB_EVENT_FLAG_ACTIVE ) == 0 ) )
+ {
+ //skip inactive ones
+ ListNext( &dev_mgr->event_list, &pevent->event_link, pnext );
+ pevent = struct_ptr( pnext, USB_EVENT, event_link );
+ }
+
+ if( pevent != NULL )
+ {
+ if( pevent->process_queue == NULL )
+ pevent->process_queue = event_list_default_process_queue;
+
+ pevent->process_queue( &dev_mgr->event_list,
+ &dev_mgr->event_pool,
+ pevent,
+ &usb_event );
+ }
+ else
+ {
+ //no active event
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ status = KeWaitForSingleObject(
+ &dev_mgr->wake_up_event,
+ Executive,
+ KernelMode,
+ TRUE,
+ &time_out // 10 minutes
+ );
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("dev_mgr_thread(): wake up, reason=0x%x\n", status ) );
+ continue;
+ }
+
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ if( usb_event.process_event )
+ {
+ usb_event.process_event( usb_event.pdev,
+ usb_event.event,
+ usb_event.context,
+ usb_event.param);
+ }
+ else
+ {
+ event_list_default_process_event( usb_event.pdev,
+ usb_event.event,
+ usb_event.context,
+ usb_event.param);
+ }
+ }
+
+ if( dev_mgr_inited )
+ {
+ for( i = 0; i < dev_mgr->hcd_count; i++ )
+ dev_mgr_disconnect_dev( dev_mgr->hcd_array[ i ]->hcd_get_root_hub( dev_mgr->hcd_array[ i ] ) );
+ dev_mgr_destroy( dev_mgr );
+ }
+ PsTerminateSystemThread( 0 );
+}
+
+VOID
+dev_mgr_timer_dpc_callback(
+PKDPC Dpc,
+PVOID context,
+PVOID SystemArgument1,
+PVOID SystemArgument2
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ LIST_HEAD templist;
+ PLIST_ENTRY pthis, pnext;
+ static ULONG ticks = 0;
+
+ ticks++;
+ dev_mgr = ( PUSB_DEV_MANAGER ) context;
+ if( dev_mgr == NULL )
+ return;
+
+ dev_mgr->timer_click ++;
+ InitializeListHead( &templist );
+
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock );
+ if( IsListEmpty( &dev_mgr->timer_svc_list ) == TRUE )
+ {
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock );
+ return;
+ }
+
+ ListFirst( &dev_mgr->timer_svc_list, pthis );
+ while( pthis )
+ {
+ ( ( PTIMER_SVC )pthis )->counter++;
+ ListNext( &dev_mgr->timer_svc_list, pthis, pnext );
+ if( ( ( PTIMER_SVC )pthis )->counter >= ( ( PTIMER_SVC )pthis )->threshold )
+ {
+ RemoveEntryList( pthis );
+ InsertTailList( &templist, pthis );
+ }
+ pthis = pnext;
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock );
+
+
+ while( IsListEmpty( &templist ) == FALSE )
+ {
+ pthis = RemoveHeadList( &templist );
+ ( ( PTIMER_SVC )pthis )->func( ( ( PTIMER_SVC )pthis )->pdev, ( PVOID )( ( PTIMER_SVC )pthis )->context );
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock );
+ free_timer_svc( &dev_mgr->timer_svc_pool, ( PTIMER_SVC )pthis );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock );
+ }
+
+}
+
+BOOL
+dev_mgr_request_timer_svc(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev,
+ULONG context,
+ULONG due_time,
+TIMER_SVC_HANDLER handler
+)
+{
+ PTIMER_SVC timer_svc;
+ KIRQL old_irql;
+
+ if( dev_mgr == NULL || pdev == NULL || due_time == 0 || handler == NULL )
+ return FALSE;
+
+ KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql );
+ timer_svc = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ if( timer_svc == NULL )
+ {
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return FALSE;
+ }
+ timer_svc->pdev = pdev;
+ timer_svc->threshold = due_time;
+ timer_svc->func = handler;
+ timer_svc->counter = 0;
+
+ InsertTailList( &dev_mgr->timer_svc_list, &timer_svc->timer_svc_link );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return TRUE;
+}
+
+BYTE
+dev_mgr_alloc_addr(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+)
+{
+ // alloc a usb addr for the device within 1-128
+ ULONG i;
+ if( dev_mgr == NULL || hcd == NULL )
+ return 0xff;
+
+ return hcd->hcd_alloc_addr( hcd );
+}
+
+BOOL
+dev_mgr_free_addr(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev,
+BYTE addr
+)
+{
+ PHCD hcd;
+ if( addr & 0x80 )
+ return FALSE;
+
+ if( dev_mgr == NULL || pdev == NULL )
+ return FALSE;
+
+ hcd = pdev->hcd;
+ if( hcd == NULL )
+ return FALSE;
+ hcd->hcd_free_addr( hcd, addr );
+ return TRUE;
+}
+
+PUSB_DEV
+dev_mgr_alloc_device(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+)
+{
+ BYTE addr;
+ PUSB_DEV pdev;
+
+ if( ( addr = dev_mgr_alloc_addr( dev_mgr, hcd ) ) == 0xff )
+ return NULL;
+
+ pdev = usb_alloc_mem( NonPagedPool, sizeof( USB_DEV ) );
+ if( pdev == NULL )
+ return NULL;
+
+ RtlZeroMemory( pdev, sizeof( USB_DEV ) );
+
+ KeInitializeSpinLock( &pdev->dev_lock );
+ dev_mgr->conn_count++;
+
+ pdev->flags = USB_DEV_STATE_RESET; //class | cur_state | low speed
+ pdev->ref_count = 0;
+ pdev->dev_addr = addr;
+
+ pdev->hcd = hcd;
+
+ pdev->dev_id = dev_mgr->conn_count; //will be used to compose dev_handle
+
+ InitializeListHead( &pdev->default_endp.urb_list );
+ pdev->default_endp.pusb_if = ( PUSB_INTERFACE )pdev;
+ pdev->default_endp.flags = USB_ENDP_FLAG_DEFAULT_ENDP; //toggle | busy-count | stall | default-endp
+
+ return pdev;
+}
+
+VOID
+dev_mgr_free_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev
+)
+{
+ if( pdev == NULL || dev_mgr == NULL )
+ return;
+
+ dev_mgr_free_addr( dev_mgr, pdev, pdev->dev_addr );
+ if( pdev->usb_config && pdev != pdev->hcd->hcd_get_root_hub( pdev->hcd ) )
+ {
+ //root hub has its config and desc buf allocated together,
+ //so no usb_config allocated seperately
+ dev_mgr_destroy_usb_config( pdev->usb_config );
+ pdev->usb_config = NULL;
+ }
+ if( pdev->desc_buf )
+ {
+ usb_free_mem( pdev->desc_buf );
+ pdev->desc_buf = NULL;
+ }
+ usb_free_mem( pdev );
+ pdev = NULL;
+ return;
+}
+
+BOOL
+rh_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ LONG i;
+ PHCD hcd;
+
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ for( i = 0; i < dev_mgr->hcd_count; i++ )
+ {
+ hcd = dev_mgr->hcd_array[ i ];
+ // if( hcd->hcd_get_type( hcd ) != HCD_TYPE_UHCI )
+ // continue;
+ rh_destroy( hcd->hcd_get_root_hub( hcd ) );
+ }
+ return TRUE;
+}
+
+BOOL
+rh_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+
+ PUSB_DEV rh;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_ENDPOINT_DESC pendp_desc;
+ PUSB_CONFIGURATION pconfig;
+ PUSB_INTERFACE pif;
+ PUSB_ENDPOINT pendp;
+ PHUB2_EXTENSION phub_ext;
+ PTIMER_SVC ptimer;
+ PURB purb;
+ NTSTATUS status;
+ PHCD hcd;
+ LONG i;
+
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ //init driver structure, no PNP table functions
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
+ pdriver->driver_desc.release_num = 0xffff; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 0; // Configuration Value
+ pdriver->driver_desc.if_num = 0; // Interface Number
+ pdriver->driver_desc.if_class = USB_CLASS_HUB; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB root hub"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_HUB;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ //pdriver->driver_init = rh_driver_init; // initialized in dev_mgr_init_driver
+ //pdriver->driver_destroy = rh_driver_destroy;
+ pdriver->disp_tbl.version = 1; // other fields of the dispatch table is not used since rh needs no pnp
+
+ pdriver->driver_ext = 0;
+ pdriver->driver_ext_size = 0;
+
+ for( i = 0; i < dev_mgr->hcd_count; i++ )
+ {
+ hcd = dev_mgr->hcd_array[ i ];
+ //if( hcd->hcd_get_type( hcd ) != HCD_TYPE_UHCI )
+ // continue;
+
+ if( ( rh = dev_mgr_alloc_device( dev_mgr, hcd ) )== NULL )
+ return FALSE;
+
+ rh->parent_dev = NULL;
+ rh->port_idx = 0;
+ rh->hcd = hcd;
+ rh->flags = USB_DEV_CLASS_ROOT_HUB | USB_DEV_STATE_CONFIGURED;
+
+ if( usb2( hcd ) )
+ rh->flags |= USB_DEV_FLAG_HIGH_SPEED;
+
+ rh->dev_driver = pdriver;
+
+ rh->desc_buf_size = sizeof( USB_DEVICE_DESC )
+ + sizeof( USB_CONFIGURATION_DESC )
+ + sizeof( USB_INTERFACE_DESC )
+ + sizeof( USB_ENDPOINT_DESC )
+ + sizeof( USB_CONFIGURATION )
+ + sizeof( HUB2_EXTENSION );
+
+ rh->desc_buf = usb_alloc_mem( NonPagedPool, rh->desc_buf_size );
+
+ if( rh->desc_buf == NULL )
+ {
+ return FALSE;
+ }
+ else
+ RtlZeroMemory( rh->desc_buf, rh->desc_buf_size );
+
+ rh->pusb_dev_desc = ( PUSB_DEVICE_DESC )rh->desc_buf;
+
+ rh->pusb_dev_desc->bLength = sizeof( USB_DEVICE_DESC );
+ rh->pusb_dev_desc->bDescriptorType = USB_DT_DEVICE;
+ rh->pusb_dev_desc->bcdUSB = 0x110;
+ if( usb2( hcd ) )
+ rh->pusb_dev_desc->bcdUSB = 0x200;
+ rh->pusb_dev_desc->bDeviceClass = USB_CLASS_HUB;
+ rh->pusb_dev_desc->bDeviceSubClass = 0;
+ rh->pusb_dev_desc->bDeviceProtocol = 0;
+ rh->pusb_dev_desc->bMaxPacketSize0 = 8;
+ if( usb2( hcd ) )
+ {
+ rh->pusb_dev_desc->bDeviceProtocol = 1;
+ rh->pusb_dev_desc->bMaxPacketSize0 = 64;
+ }
+ rh->pusb_dev_desc->idVendor = 0;
+ rh->pusb_dev_desc->idProduct = 0;
+ rh->pusb_dev_desc->bcdDevice = 0x100;
+ rh->pusb_dev_desc->iManufacturer = 0;
+ rh->pusb_dev_desc->iProduct = 0;
+ rh->pusb_dev_desc->iSerialNumber = 0;
+ rh->pusb_dev_desc->bNumConfigurations = 1;
+
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )&rh->desc_buf[ sizeof( USB_DEVICE_DESC ) ];
+ pif_desc = ( PUSB_INTERFACE_DESC ) &pconfig_desc[ 1 ];
+ pendp_desc = ( PUSB_ENDPOINT_DESC ) &pif_desc[ 1 ];
+
+ pconfig_desc->bLength = sizeof( USB_CONFIGURATION_DESC );
+ pconfig_desc->bDescriptorType = USB_DT_CONFIG;
+
+ pconfig_desc->wTotalLength = sizeof( USB_CONFIGURATION_DESC )
+ + sizeof( USB_INTERFACE_DESC )
+ + sizeof( USB_ENDPOINT_DESC );
+
+ pconfig_desc->bNumInterfaces = 1;
+ pconfig_desc->bConfigurationValue = 1;
+ pconfig_desc->iConfiguration = 0;
+ pconfig_desc->bmAttributes = 0Xe0; //self-powered and support remoke wakeup
+ pconfig_desc->MaxPower = 0;
+
+ pif_desc->bLength = sizeof( USB_INTERFACE_DESC );
+ pif_desc->bDescriptorType = USB_DT_INTERFACE;
+ pif_desc->bInterfaceNumber = 0;
+ pif_desc->bAlternateSetting = 0;
+ pif_desc->bNumEndpoints = 1;
+ pif_desc->bInterfaceClass = USB_CLASS_HUB;
+ pif_desc->bInterfaceSubClass = 0;
+ pif_desc->bInterfaceProtocol = 0;
+ pif_desc->iInterface = 0;
+
+ pendp_desc->bLength = sizeof( USB_ENDPOINT_DESC );
+ pendp_desc->bDescriptorType = USB_DT_ENDPOINT;
+ pendp_desc->bEndpointAddress = 0x81;
+ pendp_desc->bmAttributes = 0x03;
+ pendp_desc->wMaxPacketSize = 8;
+ pendp_desc->bInterval = USB_HUB_INTERVAL;
+ if( usb2( hcd ) )
+ pendp_desc->bInterval = 0x0c;
+
+ pconfig = rh->usb_config = ( PUSB_CONFIGURATION )&pendp_desc[ 1 ];
+ rh->active_config_idx = 0;
+ pconfig->pusb_config_desc = pconfig_desc;
+ pconfig->if_count = 1;
+ pconfig->pusb_dev = rh;
+ pif = &pconfig->interf[ 0 ];
+
+ pif->endp_count = 1;
+ pendp = &pif->endp[ 0 ];
+ pif->pusb_config = pconfig;;
+ pif->pusb_if_desc = pif_desc;
+
+ pif->if_ext_size = 0;
+ pif->if_ext = NULL;
+
+ phub_ext = ( PHUB2_EXTENSION )&pconfig[ 1 ];
+ phub_ext->port_count = 2;
+
+ if( usb2( hcd ) )
+ {
+ // port count is configurable in usb2
+ hcd->hcd_dispatch( hcd, HCD_DISP_READ_PORT_COUNT, &phub_ext->port_count );
+ }
+
+ {
+ int j;
+ for( j = 0; j < phub_ext->port_count; j++ )
+ {
+ psq_init( &phub_ext->port_status_queue[ j ] );
+ phub_ext->child_dev[ j ] = NULL;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_driver_init(): port[ %d ].flag=0x%x\n", \
+ j, phub_ext->port_status_queue[ j ].port_flags ) );
+ }
+ }
+
+ phub_ext->pif = pif;
+ phub_ext->hub_desc.bLength = sizeof( USB_HUB_DESCRIPTOR );
+ phub_ext->hub_desc.bDescriptorType = USB_DT_HUB;
+ phub_ext->hub_desc.bNbrPorts = ( UCHAR )phub_ext->port_count;
+ phub_ext->hub_desc.wHubCharacteristics = 0;
+ phub_ext->hub_desc.bPwrOn2PwrGood = 0;
+ phub_ext->hub_desc.bHubContrCurrent = 50;
+
+ rh->dev_ext = ( PBYTE )phub_ext;
+ rh->dev_ext_size = sizeof( HUB2_EXTENSION );
+
+ rh->default_endp.flags = USB_ENDP_FLAG_DEFAULT_ENDP;
+ InitializeListHead( &rh->default_endp.urb_list );
+ rh->default_endp.pusb_if = ( PUSB_INTERFACE )rh;
+ rh->default_endp.pusb_endp_desc = NULL; //???
+ rh->time_out_count = 0;
+ rh->error_count = 0;
+
+ InitializeListHead( &pendp->urb_list );
+ pendp->flags = 0;
+ pendp->pusb_endp_desc = pendp_desc;
+ pendp->pusb_if = pif;
+
+ //add to device list
+ InsertTailList( &dev_mgr->dev_list, &rh->dev_link );
+ hcd->hcd_set_root_hub( hcd, rh );
+ status = hub_start_int_request( rh );
+ pdriver->driver_ext = 0;
+ }
+ return TRUE;
+}
+
+BOOL
+rh_destroy(
+PUSB_DEV pdev
+)
+//to be the reverse of what init does, we assume that the timer is now killed
+//int is disconnected and the hub thread will not process event anymore
+{
+ PUSB_DEV rh;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ //???
+ rh = pdev->hcd->hcd_get_root_hub( pdev->hcd );
+ if( rh == pdev )
+ {
+ //free all the buf
+ dev_mgr_free_device(dev_mgr, rh );
+ //dev_mgr->root_hub = NULL;
+ }
+
+ return TRUE;
+}
+
+VOID
+rh_timer_svc_int_completion(
+PUSB_DEV pdev,
+PVOID context
+)
+{
+ PUSB_EVENT pevent;
+ PURB purb;
+ ULONG status, i;
+ PHCD hcd;
+ USE_IRQL;
+
+ if( pdev == NULL || context == NULL )
+ return;
+
+ purb = ( PURB )context;
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ pdev->ref_count -= 2; // one for timer_svc and one for urb, for those rh requests
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): the dev is zomb, 0x%x\n", pdev ) );
+ return;
+ }
+
+ hcd = pdev->hcd;
+ if( purb->data_length < 1 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ unlock_dev( pdev, TRUE );
+ goto LBL_OUT;
+ }
+
+ pdev->hcd->hcd_dispatch( pdev->hcd, HCD_DISP_READ_RH_DEV_CHANGE, purb->data_buffer );
+ purb->status = STATUS_SUCCESS;
+ unlock_dev( pdev, TRUE );
+
+LBL_OUT:
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, TRUE );
+ pdev->ref_count -= 2; // one for timer_svc and one for urb, for those rh requests
+ // that completed immediately, the ref_count of the dev for
+ // that urb won't increment and for normal hub request
+ // completion, hcd_generic_urb_completion will be called
+ // by the xhci_dpc_callback, and the ref_count for the urb
+ // is maintained there. So only rh's timer-svc cares refcount
+ // when hcd_generic_urb_completion is called.
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): rh's ref_count=0x%x\n", pdev->ref_count ) );
+ unlock_dev( pdev, TRUE );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): exitiing...\n" ) );
+ return;
+}
+
+VOID
+rh_timer_svc_reset_port_completion(
+PUSB_DEV pdev,
+PVOID context
+)
+{
+ PURB purb;
+ ULONG i;
+ USHORT port_num;
+ PHUB2_EXTENSION hub_ext;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ USE_IRQL;
+
+ if( pdev == NULL || context == NULL )
+ return;
+
+ dev_mgr = dev_mgr_from_dev( pdev ); //readonly and hold ref_count
+
+ //block the rh polling
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock );
+ if( IsListEmpty( &dev_mgr->timer_svc_list ) == FALSE )
+ {
+ ListFirst( &dev_mgr->timer_svc_list, pthis );
+ while( pthis )
+ {
+ if( ( ( PTIMER_SVC )pthis )->pdev == pdev && \
+ ( ( PTIMER_SVC )pthis )->threshold == RH_INTERVAL )
+ {
+ ( ( PTIMER_SVC )pthis )->threshold = RH_INTERVAL + 0x800000;
+ break;
+ }
+
+ ListNext( &dev_mgr->timer_svc_list, pthis, pnext );
+ pthis = pnext;
+ }
+ }
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock );
+
+ purb = ( PURB )context;
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ //purb->status = STATUS_ERROR;
+ //pdev->hcd->hcd_generic_urb_completion( purb, purb->context );
+
+ pdev->ref_count -= 2;
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ return;
+ }
+
+ i = pdev->hcd->hcd_rh_reset_port( pdev->hcd, ( UCHAR )psetup->wIndex );
+
+ hub_ext = hub_ext_from_dev( pdev );
+
+ {
+ USHORT temp;
+ PUCHAR pbuf;
+ if( psetup->wIndex < 16 )
+ {
+ temp = 1 << psetup->wIndex;
+ pbuf = ( PUCHAR )&temp;
+ if( temp > 128 )
+ pbuf++;
+ hub_ext->int_data_buf[ psetup->wIndex / 8 ] |= *pbuf;
+ if( i == TRUE )
+ hub_ext->rh_port_status[ psetup->wIndex ].wPortChange |= USB_PORT_STAT_C_RESET;
+ else // notify that is not a high speed device, will lost definitely
+ hub_ext->rh_port_status[ psetup->wIndex ].wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ }
+ }
+
+ //???how to construct port status map
+ // decrease the timer_svc ref-count
+ pdev->ref_count --;
+ unlock_dev( pdev, TRUE );
+
+ purb->status = STATUS_SUCCESS;
+ //we delegate the completion to the rh_timer_svc_int_completion.
+ //this function is equivalent to hub_start_reset_port_completion
+
+ usb_free_mem( purb );
+
+ //expire the rh polling timer
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->timer_svc_list_lock );
+ if( IsListEmpty( &dev_mgr->timer_svc_list ) == FALSE )
+ {
+ ListFirst( &dev_mgr->timer_svc_list, pthis );
+ while( pthis )
+ {
+ if( ( ( PTIMER_SVC )pthis )->pdev == pdev && \
+ ( ( PTIMER_SVC )pthis )->threshold == RH_INTERVAL + 0x800000 )
+ {
+ ( ( PTIMER_SVC )pthis )->counter = RH_INTERVAL;
+ ( ( PTIMER_SVC )pthis )->threshold = RH_INTERVAL;
+ break;
+ }
+
+ ListNext( &dev_mgr->timer_svc_list, pthis, pnext );
+ pthis = pnext;
+ }
+ }
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->timer_svc_list_lock );
+
+ lock_dev( pdev, TRUE );
+ pdev->ref_count--;
+ unlock_dev( pdev, TRUE );
+ return;
+}
+
+VOID
+dev_mgr_disconnect_dev(
+PUSB_DEV pdev
+)
+//called when a disconnect is detected on the port
+{
+ PLIST_ENTRY pthis, pnext;
+ PHUB2_EXTENSION phub_ext;
+ PUSB_CONFIGURATION pconfig;
+ PUSB_INTERFACE pif;
+ PUSB_DEV_MANAGER dev_mgr;
+ PHCD hcd;
+ BOOL is_hub, found;
+ ULONG dev_id;
+
+ int i;
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return;
+
+ found = FALSE;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_disconnect_dev(): entering, pdev=0x%x\n", pdev ) );
+ lock_dev( pdev, FALSE );
+ pdev->flags &= ~ USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_BEFORE_ZOMB;
+ dev_mgr = dev_mgr_from_dev( pdev );
+ unlock_dev( pdev, FALSE );
+
+ // notify dev_driver that the dev stops function before any operations
+ if( pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_stop )
+ pdev->dev_driver->disp_tbl.dev_stop( dev_mgr, dev_handle_from_dev( pdev ) );
+
+ //safe to use the dev pointer in this function.
+ lock_dev( pdev, FALSE );
+ pdev->flags &= ~ USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_ZOMB;
+ hcd = pdev->hcd;
+ dev_id = pdev->dev_id;
+ unlock_dev( pdev, FALSE );
+
+ if( dev_mgr == NULL )
+ return;
+
+ hcd->hcd_remove_device( hcd, pdev );
+
+ //disconnect its children
+ if( ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_HUB || \
+ ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_ROOT_HUB )
+ {
+ phub_ext = hub_ext_from_dev( pdev );
+ if( phub_ext )
+ {
+ for( i = 1; i <= phub_ext->port_count; i++ )
+ {
+ if( phub_ext->child_dev[ i ] )
+ {
+ dev_mgr_disconnect_dev( phub_ext->child_dev[ i ] );
+ phub_ext->child_dev[ i ] = NULL;
+ }
+ }
+ }
+ }
+
+ pconfig = pdev->usb_config;
+
+ //remove event belong to the dev
+ is_hub = ( ( pdev->flags & USB_DEV_CLASS_MASK ) == USB_DEV_CLASS_HUB );
+
+ if( phub_ext && is_hub )
+ {
+ for( i = 1; i <= phub_ext->port_count; i++ )
+ {
+ found = hub_remove_reset_event( pdev, i, FALSE );
+ if( found )
+ break;
+ }
+ }
+
+ //free event of the dev from the event list
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+ ListFirst( &dev_mgr->event_list, pthis );
+ while( pthis )
+ {
+ ListNext( &dev_mgr->event_list, pthis, pnext );
+ if( ( ( PUSB_EVENT )pthis )->pdev == pdev )
+ {
+ PLIST_ENTRY p1;
+ RemoveEntryList( pthis );
+ if( ( ( ( PUSB_EVENT )pthis )->flags & USB_EVENT_FLAG_QUE_TYPE )
+ != USB_EVENT_FLAG_NOQUE )
+ {
+ //has a queue, re-insert the queue
+ if( p1 = ( PLIST_ENTRY )( ( PUSB_EVENT )pthis )->pnext )
+ {
+ InsertHeadList( &dev_mgr->event_list, p1 );
+ free_event( &dev_mgr->event_pool, struct_ptr( pthis, USB_EVENT, event_link ) );
+ pthis = p1;
+ //note: this queue will be examined again in the next loop
+ //to find the matched dev in the queue
+ continue;
+ }
+ }
+ free_event( &dev_mgr->event_pool, struct_ptr( pthis, USB_EVENT, event_link ) );
+ }
+ else if( ( ( ( ( PUSB_EVENT )pthis )->flags & USB_EVENT_FLAG_QUE_TYPE )
+ != USB_EVENT_FLAG_NOQUE ) && ( ( PUSB_EVENT )pthis )->pnext )
+ {
+ //has a queue, examine the queue
+ PUSB_EVENT p1, p2;
+ p1 = ( PUSB_EVENT )pthis;
+ p2 = p1->pnext;
+ while( p2 )
+ {
+ if( p2->pdev == pdev )
+ {
+ p1->pnext = p2->pnext;
+ p2->pnext = NULL;
+ free_event( &dev_mgr->event_pool, p2 );
+ p2 = p1->pnext;
+ }
+ else
+ {
+ p1 = p2;
+ p2 = p2->pnext;
+ }
+ }
+ }
+ pthis = pnext;
+ }
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ // found indicates the reset event on one of the dev's port in process
+ if( found )
+ hub_start_next_reset_port( dev_mgr_from_dev( pdev ), FALSE );
+
+ // remove timer-svc belonging to the dev
+ KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql );
+ ListFirst( &dev_mgr->timer_svc_list, pthis );
+ i = 0;
+ while( pthis )
+ {
+ ListNext( &dev_mgr->timer_svc_list, pthis, pnext );
+ if( ( ( PUSB_EVENT )pthis )->pdev == pdev )
+ {
+ RemoveEntryList( pthis );
+ free_timer_svc( &dev_mgr->timer_svc_pool, struct_ptr( pthis, TIMER_SVC, timer_svc_link ) );
+ i++;
+ }
+ pthis = pnext;
+ }
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+
+ // release the refcount
+ if( i )
+ {
+ lock_dev( pdev, FALSE );
+ pdev->ref_count -= i;
+ unlock_dev( pdev, FALSE );
+ }
+
+ // wait for all the reference count be released
+ for( ; ; )
+ {
+ LARGE_INTEGER interval;
+
+ lock_dev( pdev, FALSE );
+ if( pdev->ref_count == 0 )
+ {
+ unlock_dev( pdev, FALSE );
+ break;
+ }
+ unlock_dev( pdev, FALSE );
+ // Wait two ms.
+ interval.QuadPart = -20000;
+ KeDelayExecutionThread( KernelMode, FALSE, &interval );
+ }
+
+ if( pdev->dev_driver && pdev->dev_driver->disp_tbl.dev_disconnect )
+ pdev->dev_driver->disp_tbl.dev_disconnect( dev_mgr, dev_handle_from_dev( pdev ) );
+
+ // we put it here to let handle valid before disconnect
+ KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql );
+ ListFirst( &dev_mgr->dev_list, pthis );
+ while( pthis )
+ {
+ if( ( ( PUSB_DEV )pthis ) == pdev )
+ {
+ RemoveEntryList( pthis );
+ break;
+ }
+ ListNext( &dev_mgr->dev_list, pthis, pnext );
+ pthis = pnext;
+ }
+ KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql );
+
+
+ if( pdev != pdev->hcd->hcd_get_root_hub( pdev->hcd ) )
+ {
+ dev_mgr_free_device( dev_mgr, pdev );
+ }
+ else
+ {
+ //rh_destroy( pdev );
+ //TRAP();
+ //destroy it in dev_mgr_destroy
+ }
+
+ return;
+}
+
+BOOL
+hub_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ //init driver structure, no PNP table functions
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
+ pdriver->driver_desc.release_num = 0xffff; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 0; // Configuration Value
+ pdriver->driver_desc.if_num = 0; // Interface Number
+ pdriver->driver_desc.if_class = USB_CLASS_HUB; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB hub"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_HUB;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ //pdriver->driver_init = hub_driver_init; // initialized in dev_mgr_init_driver
+ //pdriver->driver_destroy = hub_driver_destroy;
+
+ pdriver->driver_ext = 0;
+ pdriver->driver_ext_size = 0;
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = hub_connect;
+ pdriver->disp_tbl.dev_disconnect = hub_disconnect;
+ pdriver->disp_tbl.dev_stop = hub_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ return TRUE;
+}
+
+BOOL
+hub_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ pdriver->driver_ext = NULL;
+ return TRUE;
+}
+void
+
+hub_reset_pipe_completion(
+PURB purb, //only for reference, can not be released
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ NTSTATUS status;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ {
+ return;
+ }
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ return;
+ }
+
+ if( usb_error( purb->status ) )
+ {
+ //simply retry it
+ unlock_dev( pdev, TRUE );
+ //usb_free_mem( purb );
+ return;
+ }
+ unlock_dev( pdev, TRUE );
+
+ pdev = purb->pdev;
+ hub_start_int_request( pdev );
+ return;
+}
+
+NTSTATUS
+hub_start_int_request(
+PUSB_DEV pdev
+)
+{
+ PURB purb;
+ PUSB_INTERFACE pif;
+ PHUB2_EXTENSION hub_ext;
+ NTSTATUS status;
+ PHCD hcd;
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ if( purb == NULL )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_NO_MEMORY;
+ }
+
+ purb->flags = 0;
+ purb->status = STATUS_SUCCESS;
+ hub_ext = hub_ext_from_dev( pdev );
+ purb->data_buffer = hub_ext->int_data_buf;
+ purb->data_length = ( hub_ext->port_count + 7 ) / 8;
+
+ hub_if_from_dev( pdev, pif );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_start_int_request(): pdev=0x%x, pif=0x%x\n", pdev, pif ) );
+ purb->pendp = &pif->endp[ 0 ];
+ purb->pdev = pdev;
+
+ purb->completion = hub_int_completion;
+ purb->context = hub_ext;
+
+ purb->pirp = NULL;
+ purb->reference = 0;
+ hcd = pdev->hcd;
+ unlock_dev( pdev, FALSE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ }
+
+ return status;
+}
+
+void
+hub_int_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+
+ PUSB_DEV pdev;
+ PHUB2_EXTENSION hub_ext;
+ ULONG port_idx;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ LONG i;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return;
+
+ if( pcontext == NULL )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_int_completion(): entering...\n" ) );
+
+ pdev = purb->pdev;
+ hub_ext = pcontext;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ return;
+ }
+
+ hcd = pdev->hcd;
+
+ if( purb->status == STATUS_SUCCESS )
+ {
+
+ for( i = 1; i <= hub_ext->port_count; i++ )
+ {
+ if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) )
+ {
+ break;
+ }
+ }
+ if( i > hub_ext->port_count )
+ {
+ //no status change, re-initialize the int request
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ hub_start_int_request( pdev );
+ return;
+ }
+
+ port_idx = ( ULONG )i;
+
+ //re-use the urb to get port status
+ purb->pendp = &pdev->default_endp;
+ purb->data_buffer = ( PUCHAR )&hub_ext->port_status;
+
+ purb->data_length = sizeof( USB_PORT_STATUS );
+ purb->pdev = pdev;
+
+ purb->context = hub_ext;
+ purb->pdev = pdev;
+ purb->completion = hub_get_port_status_completion;
+ purb->reference = port_idx;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0xa3; //host-device class other recepient
+ psetup->bRequest = USB_REQ_GET_STATUS;
+ psetup->wValue = 0;
+ psetup->wIndex = ( USHORT )port_idx;
+ psetup->wLength = 4;
+
+ purb->pirp = NULL;
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ if( usb_error( status ) )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ }
+ else if( status == STATUS_SUCCESS )
+ {
+ // this is for root hub
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+ }
+ return;
+ }
+ else
+ {
+ unlock_dev( pdev, TRUE );
+ if( usb_halted( purb->status ) )
+ {
+ //let's reset pipe
+ usb_reset_pipe( pdev, purb->pendp, hub_reset_pipe_completion, NULL );
+ }
+ //unexpected error
+ usb_free_mem( purb );
+ purb = NULL;
+ }
+ return;
+}
+
+VOID
+hub_get_port_status_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ BYTE port_idx;
+ PHUB2_EXTENSION hub_ext;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_get_port_feature_completion(): entering...\n" ) );
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ return;
+ }
+
+ hcd = pdev->hcd;
+ if( usb_error( purb->status ) )
+ {
+ unlock_dev( pdev, TRUE );
+
+ purb->status = 0;
+ //simply retry the request refer to item 55 in document
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ if( status == STATUS_SUCCESS )
+ {
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+
+ }
+ else
+ {
+ //
+ // must be fatal error
+ // FIXME: better to pass it to the completion for further
+ // processing?
+ //
+ usb_free_mem( purb );
+ }
+ }
+ return;
+ }
+
+ hub_ext = hub_ext_from_dev( pdev );
+ port_idx = ( BYTE )purb->reference;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_get_port_stataus_completion(): port_idx=0x%x, hcd =0x%x, \
+ pdev=0x%x, purb=0x%x, hub_ext=0x%x, portsc=0x%x \n", \
+ port_idx, \
+ pdev->hcd, \
+ pdev, \
+ purb, \
+ hub_ext,\
+ *( ( PULONG ) purb->data_buffer ) ) );
+
+ psq_enqueue( &hub_ext->port_status_queue[ port_idx ],
+ *( ( PULONG )purb->data_buffer ) );
+
+ //reuse the urb to clear the feature
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->pendp = &pdev->default_endp;
+ purb->pdev = pdev;
+
+ purb->context = ( PVOID )&hub_ext->port_status ;
+ purb->pdev = pdev;
+ purb->completion = hub_clear_port_feature_completion;
+ purb->reference = port_idx;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0x23; //host-device class port recepient
+ psetup->bRequest = USB_REQ_CLEAR_FEATURE;
+ psetup->wIndex = port_idx;
+ psetup->wLength = 0;
+ purb->pirp = NULL;
+
+ if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_CONNECTION;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_ENABLE;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_SUSPEND;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_RESET;
+ }
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+
+ // if( status != STATUS_SUCCESS )
+ if( status != STATUS_PENDING )
+ {
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+ }
+ /*else if( usb_error( status ) )
+ {
+ usb_free_mem( purb );
+ return;
+ }*/
+ return;
+
+}
+
+VOID
+hub_clear_port_feature_completion(
+PURB purb,
+PVOID context
+)
+{
+ BYTE port_idx;
+ LONG i;
+ BOOL bReset, event_post, brh;
+ ULONG pc;
+ PHCD hcd;
+ NTSTATUS status;
+ PUSB_DEV pdev, pdev2;
+ PUSB_EVENT pevent;
+ PUSB_ENDPOINT pendp;
+ PUSB_INTERFACE pif;
+ PHUB2_EXTENSION hub_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return;
+
+ if( context == NULL )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_clear_port_feature_completion(): entering...\n" ) );
+
+ pdev = purb->pdev;
+ port_idx = ( BYTE )purb->reference;
+
+ lock_dev( pdev, TRUE );
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+ brh = ( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB );
+
+ if( usb_error( purb->status ) )
+ {
+ unlock_dev( pdev, TRUE );
+
+ purb->status = 0;
+
+ // retry the request
+ status = hcd->hcd_submit_urb( hcd, purb->pdev, purb->pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ if( status == STATUS_SUCCESS )
+ {
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+ }
+ else
+ {
+ //
+ // FIXME: should we pass the error to the completion directly
+ // instead of forstall it here?
+ //
+ // do not think the device is workable, no requests to it any more.
+ // including the int polling
+ //
+ // usb_free_mem( purb );
+ //
+ goto LBL_SCAN_PORT_STAT;
+ }
+ }
+ return;
+ }
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ return;
+ }
+
+ pc = ( ( PUSB_PORT_STATUS ) context )->wPortChange;
+
+ if( pc )
+ {
+ // the bits are tested in ascending order
+ if( pc & USB_PORT_STAT_C_CONNECTION )
+ {
+ pc &= ~USB_PORT_STAT_C_CONNECTION;
+ }
+ else if( pc & USB_PORT_STAT_C_ENABLE )
+ {
+ pc &= ~USB_PORT_STAT_C_ENABLE;
+ }
+ else if( pc & USB_PORT_STAT_C_SUSPEND )
+ {
+ pc &= ~USB_PORT_STAT_C_SUSPEND;
+ }
+ else if( pc & USB_PORT_STAT_C_OVERCURRENT )
+ {
+ pc &= ~USB_PORT_STAT_C_OVERCURRENT;
+ }
+ else if( pc & USB_PORT_STAT_C_RESET )
+ {
+ pc &= ~USB_PORT_STAT_C_RESET;
+ }
+ }
+ ( ( PUSB_PORT_STATUS ) context )->wPortChange = ( USHORT )pc;
+
+ hub_ext = hub_ext_from_dev( pdev );
+
+ if( pc )
+ {
+ //some other status change on the port still active
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_CONNECTION )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_CONNECTION;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_ENABLE )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_ENABLE;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_SUSPEND )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_SUSPEND;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_OVERCURRENT )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_OVER_CURRENT;
+ }
+ else if( hub_ext->port_status.wPortChange & USB_PORT_STAT_C_RESET )
+ {
+ psetup->wValue = USB_PORT_FEAT_C_RESET;
+ }
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ if( status == STATUS_SUCCESS )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_clear_port_stataus_completion(): port_idx=0x%x, hcd=0x%x, \
+ pdev=0x%x, purb=0x%x, hub_ext=0x%x, wPortChange=0x%x \n", \
+ port_idx, \
+ pdev->hcd, \
+ pdev, \
+ purb, \
+ hub_ext,\
+ pc ) );
+
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+ }
+ else
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, (" hub_clear_port_feature_completion(): \
+ error=0x%x\n", status ) );
+
+ // usb_free_mem( purb );
+ goto LBL_SCAN_PORT_STAT;
+ }
+ }
+ return;
+ }
+
+ for( i = 1; i <= hub_ext->port_count; i++ )
+ {
+ if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) )
+ {
+ break;
+ }
+ }
+
+ //clear the port-change map, we have get port i's status.
+ hub_ext->int_data_buf[ i >> 3 ] &= ~( 1 << i );
+
+ //rescan to find some other port that has status change
+ for( i = 1; i <= hub_ext->port_count; i++ )
+ {
+ if( hub_ext->int_data_buf[ i >> 3 ] & ( 1 << i ) )
+ {
+ break;
+ }
+ }
+
+ if( i <= hub_ext->port_count )
+ {
+ //still has port-change pending, get the port status change
+ port_idx = ( UCHAR )i;
+
+ //re-use the urb
+ purb->data_buffer = ( PUCHAR )&hub_ext->port_status;
+ purb->data_length = sizeof( USB_PORT_STATUS );
+ purb->pendp = &pdev->default_endp;
+ purb->pdev = pdev;
+
+ purb->context = hub_ext;
+ purb->pdev = pdev;
+ purb->completion = hub_get_port_status_completion;
+ purb->reference = port_idx;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0xa3; //host-device class other recepient
+ psetup->bRequest = USB_REQ_GET_STATUS;
+ psetup->wValue = 0;
+ psetup->wIndex = port_idx;
+ psetup->wLength = 4;
+
+ purb->pirp = NULL;
+
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ if( status == STATUS_SUCCESS )
+ {
+ hcd->hcd_generic_urb_completion( purb, purb->context );
+ }
+ else
+ { //must be fatal error
+ // usb_free_mem( purb );
+ goto LBL_SCAN_PORT_STAT;
+ }
+ }
+ return;
+ }
+
+ unlock_dev( pdev, TRUE );
+
+LBL_SCAN_PORT_STAT:
+
+ //all status changes are cleared
+ if( purb )
+ usb_free_mem( purb );
+
+ purb = NULL;
+
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ //
+ // if reset is in process, the dev_mgr_disconnect_dev will continue
+ // the following resets
+ //
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ return;
+ }
+
+ //at last we wake up thread if some port have status change to process
+ port_idx = 0;
+ for( i = 1, event_post = FALSE; i <= hub_ext->port_count; i++ )
+ {
+ if( psq_is_empty( &hub_ext->port_status_queue[ i ]) == FALSE )
+ {
+ if( port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_IDLE ||
+ port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_WAIT_ADDRESSED )
+ {
+ // have status in the queue pending
+ // STATE_WAIT_ADDRESSED is added to avoid some bad mannered
+ // hub to disturb the reset process
+ hub_post_esq_event( pdev, ( BYTE )i, hub_event_examine_status_que );
+ }
+ else if( port_state( hub_ext->port_status_queue[ i ].port_flags ) == STATE_WAIT_RESET_COMPLETE )
+ {
+ //there is only one reset at one time
+ port_idx = ( BYTE )i;
+ }
+ }
+ }
+
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+
+
+ if( port_idx )
+ hub_check_reset_port_status(
+ pdev,
+ port_idx );
+
+ //reinitialize the int request, here to reduce some uncertainty of concurrency
+ hub_start_int_request( pdev );
+
+ return;
+}
+
+VOID
+hub_event_examine_status_que(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context, //hub_ext
+ULONG param //port_idx
+)
+{
+ PHUB2_EXTENSION hub_ext;
+ USB_PORT_STATUS ps;
+ PUSB_DEV pchild_dev;
+ PTIMER_SVC ptimer;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ USE_IRQL;
+
+ if( pdev == NULL || context == 0 || param == 0 )
+ return;
+
+ while( TRUE )
+ {
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ break;
+ }
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hub_ext = hub_ext_from_dev( pdev );
+
+ if( psq_is_empty( &hub_ext->port_status_queue[ param ] ) )
+ {
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_IDLE );
+ unlock_dev( pdev, FALSE );
+ break;
+ }
+
+ *( ( ULONG* )&ps ) = psq_outqueue( &hub_ext->port_status_queue[ param ] );
+
+
+ pchild_dev = hub_ext->child_dev[ param ];
+ hub_ext->child_dev[ param ] = 0;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_event_examine_status_queue(): dev_addr=0x%x, port=0x%x, wPortChange=0x%x, wPortStatus=0x%x\n", \
+ pdev->dev_addr, \
+ param,
+ ps.wPortChange, \
+ ps.wPortStatus ) );
+
+ unlock_dev( pdev, FALSE );
+
+ if( pchild_dev != NULL )
+ dev_mgr_disconnect_dev( pchild_dev );
+
+ if( ( ( ps.wPortChange & USB_PORT_STAT_C_ENABLE ) &&
+ ( ( pdev->flags & USB_DEV_CLASS_MASK ) != USB_DEV_CLASS_ROOT_HUB ) )
+ || ( ps.wPortChange & USB_PORT_STAT_C_OVERCURRENT )
+ || ( ps.wPortChange & USB_PORT_STAT_C_RESET )
+ || ( ( ps.wPortChange & USB_PORT_STAT_C_CONNECTION ) &&
+ !( ps.wPortStatus & USB_PORT_STAT_CONNECTION ) ) )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): error occured, portc=0x%x, ports=0x%x\n", \
+ ps.wPortChange,\
+ ps.wPortStatus ) );
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ break;
+ }
+ if( psq_is_empty( &hub_ext->port_status_queue[ param ] ) )
+ {
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_IDLE );
+ }
+ else
+ {
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_EXAMINE_STATUS_QUE );
+ }
+ unlock_dev( pdev, FALSE );
+ continue;
+
+ }
+ else if( ( ps.wPortChange & USB_PORT_STAT_C_CONNECTION )
+ && ( ps.wPortStatus & USB_PORT_STAT_CONNECTION )
+ && psq_is_empty( &hub_ext->port_status_queue[ param ] ) )
+ {
+ KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql );
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): dev lost\n" ) );
+ break;
+ }
+ ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ if( ptimer == NULL )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): timer can not allocated\n" ) );
+ break;
+ }
+
+ //a new connection
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): new connection comes\n" ) );
+
+ ptimer->counter = 0;
+ ptimer->threshold = 21; //100 ms
+
+ if( ps.wPortStatus & USB_PORT_STAT_LOW_SPEED )
+ ptimer->threshold = 51; //500 ms
+
+ ptimer->context = param;
+ ptimer->pdev = pdev;
+ ptimer->func = hub_timer_wait_dev_stable;
+ InsertTailList( &dev_mgr->timer_svc_list, &ptimer->timer_svc_link );
+ pdev->ref_count ++;
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_WAIT_STABLE );
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ break;
+
+ }
+ else
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_event_examine_status_queue(): unknown error\n" ) );
+ continue;
+ }
+ }
+ return;
+}
+
+VOID
+hub_timer_wait_dev_stable(
+PUSB_DEV pdev,
+PVOID context //port-index
+)
+{
+
+ PHUB2_EXTENSION hub_ext;
+ PUSB_INTERFACE pif;
+ PUSB_EVENT pevent;
+ ULONG param;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ USE_IRQL;
+
+ if( pdev == NULL || context == 0 )
+ return;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ param = ( ULONG ) context;
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ lock_dev( pdev, TRUE );
+
+ pdev->ref_count--;
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ goto LBL_OUT;
+ }
+
+ hub_ext = hub_ext_from_dev( pdev );
+
+ if( !psq_is_empty( &hub_ext->port_status_queue[ param ] ) )
+ {
+ //error occured, normally we should not receive event here
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_EXAMINE_STATUS_QUE );
+
+ hub_post_esq_event( pdev, ( BYTE )param, hub_event_examine_status_que );
+ }
+ else
+ {
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags,
+ STATE_WAIT_RESET );
+
+ hub_post_esq_event( pdev, ( BYTE )param, hub_event_dev_stable );
+
+ }
+
+ LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ return;
+}
+
+VOID
+hub_event_dev_stable(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context, //hub_ext
+ULONG param //port_idx
+)
+{
+
+ PHUB2_EXTENSION hub_ext;
+ PUSB_EVENT pevent, pevent1;
+ PLIST_ENTRY pthis, pnext;
+ BOOL que_exist;
+ PHCD hcd;
+ PUSB_DEV_MANAGER dev_mgr;
+ NTSTATUS status;
+ PURB purb;
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ USE_IRQL;
+
+ if( pdev == NULL || context == 0 || param == 0 )
+ return;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ goto LBL_OUT;
+
+ hub_ext = hub_ext_from_dev( pdev );
+ hcd = pdev->hcd;
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ goto LBL_OUT;
+
+ pevent->event = USB_EVENT_WAIT_RESET_PORT;
+ pevent->pdev = pdev;
+ pevent->context = ( ULONG )hub_ext;
+ pevent->param = param;
+ pevent->flags = USB_EVENT_FLAG_QUE_RESET;
+ pevent->process_event = NULL; //hub_event_reset_port_complete;
+ pevent->process_queue = NULL; //hub_event_reset_process_queue;
+ pevent->pnext = NULL;
+
+ ListFirst( &dev_mgr->event_list, pthis );
+ que_exist = FALSE;
+
+ while( pthis )
+ {
+ //insert the event in to the wait-queue
+ pevent1 = ( PUSB_EVENT ) pthis;
+ if( pevent1->event == USB_EVENT_WAIT_RESET_PORT )
+ {
+ while( pevent1->pnext )
+ pevent1 = pevent1->pnext;
+
+ pevent1->pnext = pevent;
+ que_exist = TRUE;
+ break;
+ }
+ ListNext( &dev_mgr->event_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ if( !que_exist )
+ {
+ //Let's start a reset port request
+ InsertHeadList( &dev_mgr->event_list, &pevent->event_link );
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->pendp = &pdev->default_endp;
+
+ purb->context = hub_ext;
+ purb->pdev = pdev;
+ purb->completion = hub_start_reset_port_completion; //hub_int_completion;
+ purb->reference = param;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0x23; //host-device other recepient
+ psetup->bRequest = USB_REQ_SET_FEATURE;
+ psetup->wValue = USB_PORT_FEAT_RESET;
+ psetup->wIndex = ( USHORT )param;
+ psetup->wLength = 0;
+
+ purb->pirp = NULL;
+ //enter another state
+ set_port_state( hub_ext->port_status_queue[ param ].port_flags, STATE_WAIT_RESET_COMPLETE );
+
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) ;
+ if( status != STATUS_PENDING )
+ {
+ //must be fatal error
+ usb_free_mem( purb );
+ hub_reexamine_port_status_queue( pdev, param, FALSE );
+ if( hub_remove_reset_event( pdev, param, FALSE ) )
+ hub_start_next_reset_port( dev_mgr, FALSE );
+ }
+ return;
+ }
+
+ LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ return;
+}
+
+VOID
+hub_start_reset_port_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ PUSB_DEV_MANAGER dev_mgr;
+ NTSTATUS status;
+ ULONG port_idx;
+ PHCD hcd;
+
+ USE_IRQL;
+ if( purb == NULL )
+ return;
+
+ if( context == NULL )
+ {
+ //fatal error no retry.
+ usb_free_mem( purb );
+ return;
+ }
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+ port_idx = purb->reference;
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ goto LBL_FREE_EVENT;
+ }
+
+ hcd = pdev->hcd;
+ dev_mgr = dev_mgr_from_dev( pdev );
+ unlock_dev( pdev, TRUE );
+
+ status = purb->status;
+ usb_free_mem( purb );
+
+ if( !usb_error( status ) )
+ {
+ return;
+ }
+
+ LBL_FREE_EVENT:
+ //since we have no patient to retry the dev, we should remove the event of
+ //wait_reset_port on the port from the event list. and if possible, start
+ //another reset process. note other port on the dev still have chance to be
+ //reset if necessary.
+ hub_reexamine_port_status_queue( pdev, port_idx, TRUE );
+ if( hub_remove_reset_event( pdev, port_idx, TRUE ) )
+ hub_start_next_reset_port( dev_mgr, TRUE );
+ return;
+}
+
+
+VOID
+hub_set_address_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev, hub_dev;
+ PUSB_ENDPOINT pendp;
+ PUSB_DEV_MANAGER dev_mgr;
+ NTSTATUS status;
+ ULONG port_idx;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return;
+
+ if( context == NULL )
+ {
+ //fatal error no retry.
+ usb_free_mem( purb );
+ return;
+ }
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+ port_idx = purb->reference;
+
+ lock_dev( pdev, TRUE );
+
+ hcd = pdev->hcd;
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hub_dev = pdev->parent_dev;
+ port_idx = pdev->port_idx;
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ //some error occured, let's start the next reset event
+ goto LBL_RESET_NEXT;
+ }
+
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_ADDRESSED;
+
+ unlock_dev( pdev, TRUE );
+ status = purb->status;
+
+ if( usb_error( status ) )
+ {
+ //retry the urb
+ purb->status = 0;
+ hcd_dbg_print( DBGLVL_MAXIMUM, ( "hub_set_address_completion: can not set address\n" ) );
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ //some error occured, disable the port
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ status = hub_disable_port_request( hub_dev, ( UCHAR )port_idx );
+ }
+ return;
+ }
+
+ usb_free_mem( purb );
+ //let address settle
+ usb_wait_ms_dpc( 10 );
+
+ //let's config the dev
+ dev_mgr_start_config_dev( pdev );
+
+ LBL_RESET_NEXT:
+
+ //second, remove the event in the queue
+ hub_reexamine_port_status_queue( hub_dev, port_idx, TRUE );
+ if( hub_remove_reset_event( hub_dev, port_idx, TRUE ) )
+ hub_start_next_reset_port( dev_mgr, TRUE );
+ return;
+};
+
+VOID
+hub_disable_port_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+ PHUB2_EXTENSION hub_ext;
+ PUSB_DEV pdev;
+ PUSB_DEV_MANAGER dev_mgr;
+ UCHAR port_idx;
+ PUSB_ENDPOINT pendp;
+ PUSB_CTRL_SETUP_PACKET psetup;
+
+ if( purb == NULL )
+ return;
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ port_idx = ( UCHAR )psetup->wIndex;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ usb_free_mem( purb );
+
+ hub_reexamine_port_status_queue( pdev, port_idx, TRUE );
+ if( hub_remove_reset_event( pdev, port_idx, TRUE ) )
+ hub_start_next_reset_port( dev_mgr, TRUE );
+
+ return;
+}
+
+NTSTATUS
+hub_disable_port_request(
+PUSB_DEV pdev,
+UCHAR port_idx
+)
+//caller should guarantee the validity of the dev
+{
+ PURB purb;
+ PUSB_ENDPOINT pendp;
+ PHUB2_EXTENSION hub_ext;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ PHCD hcd;
+ USE_IRQL;
+
+ if( pdev == NULL || port_idx == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ purb->flags = 0;
+ purb->status = STATUS_SUCCESS;
+
+ hub_ext = hub_ext_from_dev( pdev );
+
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+
+ pendp = purb->pendp = &pdev->default_endp;
+ purb->pdev = pdev;
+
+ purb->completion = hub_disable_port_completion;
+ purb->context = hub_ext;
+
+ purb->pirp = NULL;
+ purb->reference = 0;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0x23; //host-device other recepient
+ psetup->bRequest = USB_REQ_CLEAR_FEATURE; //clear_feature
+ psetup->wValue = USB_PORT_FEAT_ENABLE;
+ psetup->wIndex = ( USHORT )port_idx;
+ psetup->wLength = 0;
+
+ purb->pirp = NULL;
+ //enter another state
+ hcd = pdev->hcd;
+ unlock_dev( pdev, FALSE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ if( status == STATUS_PENDING )
+ return status;
+
+ usb_free_mem( purb );
+ return status;
+}
+
+
+
+BOOL
+hub_remove_reset_event(
+PUSB_DEV pdev,
+ULONG port_idx,
+BOOL from_dpc
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_EVENT pevent, pnext_event;
+ BOOL found;
+
+ KIRQL old_irql;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ if( port_idx == 0 )
+ return FALSE;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ found = FALSE;
+
+ if( from_dpc )
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+
+ ListFirst( &dev_mgr->event_list, pthis );
+ while( pthis )
+ {
+ pevent = ( PUSB_EVENT )pthis;
+ if( pevent->event == USB_EVENT_WAIT_RESET_PORT &&
+ ( pevent->flags & USB_EVENT_FLAG_QUE_TYPE ) == USB_EVENT_FLAG_QUE_RESET )
+ {
+ if( pevent->pdev == pdev && pevent->param == port_idx )
+ {
+ //remove it
+ RemoveEntryList( &pevent->event_link );
+ pnext_event = pevent->pnext;
+ free_event( &dev_mgr->event_pool, pevent );
+
+ if( pnext_event )
+ InsertHeadList( &dev_mgr->event_list, &pnext_event->event_link );
+
+ found = TRUE;
+ break;
+ }
+ }
+ ListNext( &dev_mgr->event_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ if( from_dpc )
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ return found;
+}
+
+BOOL
+hub_start_next_reset_port(
+PUSB_DEV_MANAGER dev_mgr,
+BOOL from_dpc
+)
+{
+ PLIST_ENTRY pthis, pnext;
+ PUSB_EVENT pevent, pnext_event;
+ PUSB_DEV pdev;
+ PHUB2_EXTENSION hub_ext;
+ BOOL bret;
+ PURB purb;
+ BOOL processed;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ bret = FALSE;
+ processed = FALSE;
+
+ if( from_dpc )
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+
+ ListFirst( &dev_mgr->event_list, pthis);
+
+ while( pevent = ( PUSB_EVENT )pthis )
+ {
+ while( pevent->event == USB_EVENT_WAIT_RESET_PORT &&
+ ( pevent->flags & USB_EVENT_FLAG_QUE_TYPE ) == USB_EVENT_FLAG_QUE_RESET )
+ {
+
+ processed = TRUE;
+
+ pdev = pevent->pdev;
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ pnext_event = pevent->pnext;
+ free_event( &dev_mgr->event_pool, pevent );
+ pevent = pnext_event;
+ if( pevent == NULL )
+ {
+ bret = FALSE;
+ break;
+ }
+ continue;
+ }
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->pendp = &pdev->default_endp;
+
+ hub_ext = hub_ext_from_dev( pdev );
+ purb->context = hub_ext;
+ purb->pdev = pdev;
+ purb->completion = hub_start_reset_port_completion;
+ purb->reference = pevent->param;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ psetup->bmRequestType = 0x23; //host-device other recepient
+ psetup->bRequest = 3; //set_feature
+ psetup->wValue = USB_PORT_FEAT_RESET;
+ psetup->wIndex = ( USHORT )pevent->param;
+ psetup->wLength = 0;
+
+ purb->pirp = NULL;
+ hcd = pdev->hcd;
+ set_port_state( hub_ext->port_status_queue[ pevent->param ].port_flags, STATE_WAIT_RESET_COMPLETE );
+ unlock_dev( pdev, TRUE );
+
+ bret = TRUE;
+ break;
+ }
+
+ if( !processed )
+ {
+ ListNext( &dev_mgr->event_list, pthis, pnext );
+ pthis = pnext;
+ }
+ else
+ break;
+ }
+
+ if( from_dpc )
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ if( processed && bret )
+ {
+ if( hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) != STATUS_PENDING )
+ {
+ //fatal error
+ usb_free_mem( purb );
+ bret = FALSE;
+ //do not know what to do
+ }
+ }
+
+ if( pthis == NULL )
+ bret = TRUE;
+
+ return bret;
+}
+
+//
+//must have event-list-lock and dev-lock acquired
+//
+VOID
+hub_post_esq_event(
+PUSB_DEV pdev,
+BYTE port_idx,
+PROCESS_EVENT pe
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_EVENT pevent;
+
+ if( pdev == NULL || port_idx == 0 || pe == NULL )
+ return;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ pevent->event = USB_EVENT_DEFAULT;
+ pevent->process_queue = event_list_default_process_queue;
+ pevent->process_event = pe;
+ pevent->context = ( ULONG )hub_ext_from_dev( pdev );
+ pevent->param = port_idx;
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->pdev = pdev;
+ pevent->pnext = NULL;
+
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ // usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_post_esq_event(): current element in event list is 0x%x\n", \
+ // dbg_count_list( &dev_mgr->event_list ) ) );
+ return;
+
+}
+
+BOOL
+hub_check_reset_port_status(
+PUSB_DEV pdev,
+LONG port_idx
+)
+// called only in hub_clear_port_feature_completion
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PHUB2_EXTENSION hub_ext;
+ BOOL bReset;
+ USB_PORT_STATUS port_status;
+ PUSB_DEV pdev2;
+ PURB purb2;
+ PHCD hcd;
+
+ PUSB_CTRL_SETUP_PACKET psetup;
+ ULONG status;
+ LARGE_INTEGER delay;
+
+ USE_IRQL;
+
+ //let's check whether the status change is a reset complete
+ usb_dbg_print( DBGLVL_MAXIMUM, ("hub_check_reset_port_status(): entering...\n" ) );
+ dev_mgr = dev_mgr_from_dev( pdev );
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->dev_list_lock );
+ lock_dev( pdev, TRUE );
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock );
+ return FALSE;
+ }
+
+ hub_ext = hub_ext_from_dev( pdev );
+ port_status = psq_peek( &hub_ext->port_status_queue[ port_idx ], 0 );
+
+ bReset = FALSE;
+ if( port_status.wPortChange & USB_PORT_STAT_C_RESET )
+ bReset = TRUE;
+
+ pdev2 = NULL;
+ purb2 = NULL;
+
+ if( bReset
+ && port_state( hub_ext->port_status_queue[ port_idx ].port_flags ) == STATE_WAIT_RESET_COMPLETE
+ && psq_count( &hub_ext->port_status_queue[ port_idx ] ) == 1 )
+ {
+ // a port-reset complete, empty the queue, keep the state
+ psq_outqueue( &hub_ext->port_status_queue[ port_idx ] );
+ set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags,
+ STATE_WAIT_ADDRESSED );
+
+ //let's new a dev, and start the set-addr request
+ if( hub_ext->child_dev[ port_idx ] == 0 )
+ {
+ pdev2 = hub_ext->child_dev[ port_idx ]
+ = dev_mgr_alloc_device( dev_mgr, hcd );
+ if( pdev2 )
+ {
+ purb2 = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( !purb2 )
+ {
+ dev_mgr_free_device( dev_mgr, pdev2 );
+ pdev2 = hub_ext->child_dev[ port_idx ] = NULL;
+ }
+ else
+ {
+ if( port_status.wPortStatus & USB_PORT_STAT_LOW_SPEED )
+ {
+ pdev2->flags |= USB_DEV_FLAG_LOW_SPEED;
+ }
+ else if( port_status.wPortStatus & USB_PORT_STAT_HIGH_SPEED )
+ pdev2->flags |= USB_DEV_FLAG_HIGH_SPEED;
+
+ pdev2->parent_dev = pdev;
+ pdev2->port_idx = ( UCHAR )port_idx;
+ pdev2->ref_count++;
+
+ RtlZeroMemory( purb2, sizeof( URB ) );
+
+ purb2->pdev = pdev2;
+ purb2->pendp = &pdev2->default_endp;
+ purb2->context = hub_ext;
+ purb2->completion = hub_set_address_completion;
+
+ InitializeListHead( &purb2->trasac_list );
+ purb2->reference = port_idx;
+ purb2->pirp = 0;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb2->setup_packet;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_ADDRESS;
+ psetup->wValue = pdev2->dev_addr;
+ }
+ }
+ }
+
+ if( pdev2 && purb2 )
+ {
+ //creation success, emit the urb
+ //add to dev list
+ InsertTailList( &dev_mgr->dev_list, &pdev2->dev_link );
+
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock );
+
+ status = hcd->hcd_submit_urb( hcd, pdev2, purb2->pendp, purb2 );
+
+ lock_dev( pdev2, TRUE );
+ pdev2->ref_count--;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_check_reset_port_status(): new dev ref_count=0x%x\n", pdev2->ref_count ) );
+ unlock_dev( pdev2, TRUE );
+
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb2 );
+ //??? do we need to lock it for SMP?
+ //dev_mgr_free_device( dev_mgr, pdev2 ), let dev_mgr_thread to clean it;
+ // disable the port
+ if( hub_disable_port_request( pdev, ( UCHAR )port_idx ) != STATUS_PENDING )
+ goto LBL_RESET_FAIL;
+ }
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_check_reset_port_status(): not a correct reset status\n" ) );
+ }
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->dev_list_lock );
+
+LBL_RESET_FAIL:
+ //Any event other than reset cause the reset process stall and another
+ //pending reset-port requeset is serviced
+ hub_reexamine_port_status_queue( pdev, port_idx, TRUE );
+ if( hub_remove_reset_event( pdev, port_idx, TRUE ) )
+ hub_start_next_reset_port( dev_mgr, TRUE);
+
+ return FALSE;
+}
+
+VOID
+hub_reexamine_port_status_queue(
+PUSB_DEV hub_dev,
+ULONG port_idx,
+BOOL from_dpc
+)
+{
+
+ PHUB2_EXTENSION hub_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+
+ USE_IRQL;
+
+ if( hub_dev == NULL || port_idx == 0 )
+ return;
+
+ dev_mgr = dev_mgr_from_dev( hub_dev );
+ if( from_dpc )
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+
+ lock_dev( hub_dev, TRUE );
+ if( dev_state( hub_dev ) != USB_DEV_STATE_ZOMB )
+ {
+
+ hub_ext = hub_ext_from_dev( hub_dev );
+ if( psq_is_empty( &hub_ext->port_status_queue[ port_idx ] ) )
+ {
+ set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags,
+ STATE_IDLE );
+
+ }
+ else
+ {
+ set_port_state( hub_ext->port_status_queue[ port_idx ].port_flags,
+ STATE_EXAMINE_STATUS_QUE );
+
+ hub_post_esq_event( hub_dev, ( UCHAR )port_idx, hub_event_examine_status_que );
+ }
+ }
+ unlock_dev( hub_dev, TRUE );
+
+ if( from_dpc )
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ else
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ return;
+}
+
+BOOL
+dev_mgr_start_config_dev(
+PUSB_DEV pdev
+)
+//called in hub_set_address_completion
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PBYTE data_buf;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PURB purb;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ return FALSE;
+ }
+
+ hcd = pdev->hcd;
+
+ //first, get device descriptor
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ data_buf = usb_alloc_mem( NonPagedPool, 512 );
+ if( purb == NULL )
+ {
+ unlock_dev( pdev, TRUE );
+ return FALSE;
+ }
+
+ RtlZeroMemory( purb, sizeof( URB ) );
+ RtlZeroMemory( data_buf, 512 );
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ purb->data_buffer = data_buf; // user data
+ purb->data_length = 8; // get partial desc
+
+ pdev->desc_buf = data_buf;
+ pdev->desc_buf_size = 512;
+
+ purb->pdev = pdev;
+ purb->pendp = &pdev->default_endp; //pipe for current transfer
+
+ purb->completion = dev_mgr_get_desc_completion;
+ purb->reference = 0;
+
+ InitializeListHead( &purb->trasac_list );
+
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = ( USB_DT_DEVICE << 8 ) | 0;
+ psetup->wIndex = 0;
+ psetup->wLength = 8; //sizeof( USB_DEVICE_DESC );
+ unlock_dev( pdev, TRUE );
+
+ if( hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb ) != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ usb_free_mem( data_buf );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+VOID
+dev_mgr_get_desc_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_ENDPOINT pendp;
+ PUSB_DEV_MANAGER dev_mgr;
+ NTSTATUS status;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ LONG i;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return;
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+
+ if( pdev == NULL || pendp == NULL )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ return;
+ }
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ goto LBL_OUT;
+ }
+
+ pendp = &pdev->default_endp;
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ if( usb_error( purb->status ) )
+ {
+ unlock_dev( pdev, TRUE );
+ hcd_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_get_desc_completion: can not get dev desc ref=0x%x, status=0x%x\n", purb->reference, purb->status ) );
+ goto LBL_OUT;
+ }
+
+ switch( purb->reference )
+ {
+ case 0:
+ {
+ //only partial dev_desc
+ //enable the dev specific default endp maxpacketsize
+ pdev->pusb_dev_desc = ( PUSB_DEVICE_DESC )purb->data_buffer;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ psetup->wLength = sizeof( USB_DEVICE_DESC );
+
+ //get the complete dev_desc
+ purb->reference = 1;
+ purb->status = 0;
+ purb->data_length = sizeof( USB_DEVICE_DESC );
+
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ goto LBL_OUT;
+ }
+ return;
+ }
+ case 1:
+ {
+ //let's begin to get config descriptors.
+ if( pdev->pusb_dev_desc->bNumConfigurations == 0 )
+ {
+ unlock_dev( pdev, TRUE );
+ goto LBL_OUT;
+ }
+
+ purb->data_buffer += sizeof( USB_DEVICE_DESC );
+ purb->data_length = 8;
+ purb->reference++;
+ purb->context = ( PVOID )sizeof( USB_DEVICE_DESC );
+ purb->status = 0;
+
+ psetup->wValue = ( USB_DT_CONFIG << 8 ) | 0;
+ psetup->wLength = 8;
+ unlock_dev( pdev, TRUE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+
+ if( status != STATUS_PENDING )
+ {
+ goto LBL_OUT;
+ }
+ return;
+ }
+ default:
+ {
+ LONG config_idx;
+ config_idx = ( purb->reference >> 1 ) - 1;
+ if( ( purb->reference & 1 ) == 0 )
+ {
+ //partial config desc is obtained.
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )purb->data_buffer;
+ if( pconfig_desc->wTotalLength >= 1024 )
+ {
+ //treat as an error
+ unlock_dev( pdev, TRUE );
+ goto LBL_OUT;
+
+ }
+
+ if( pconfig_desc->wTotalLength > ( USHORT )( pdev->desc_buf_size - ( LONG ) purb->context ) )
+ {
+ //rewind the 8-byte hdr
+ *( ( PULONG )&context ) -= 8;
+ realloc_buf( pdev, purb );
+ }
+ purb->data_length = pconfig_desc->wTotalLength;
+ psetup->wLength = pconfig_desc->wTotalLength;
+ purb->reference ++;
+ unlock_dev( pdev, TRUE );
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ if( status != STATUS_PENDING )
+ goto LBL_OUT;
+
+ }
+ else
+ {
+ //complete desc is returned.
+ if( config_idx + 1 < pdev->pusb_dev_desc->bNumConfigurations )
+ {
+ //still have configurations left
+ *( ( PULONG )&context ) += psetup->wLength;
+ purb->data_buffer = &pdev->desc_buf[ ( LONG ) context ];
+ purb->data_length = 8;
+ psetup->wLength = 8;
+ psetup->wValue = ( ( ( USB_DT_CONFIG ) << 8 ) | ( config_idx + 1 ) );
+ purb->reference ++;
+ purb->context = context;
+
+ if( ( ( LONG )context ) + 8 > pdev->desc_buf_size )
+ realloc_buf( pdev, purb );
+
+ purb->status = 0;
+ unlock_dev( pdev, TRUE );
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb );
+ if( status != STATUS_PENDING )
+ goto LBL_OUT;
+ }
+ else
+ {
+ //config descriptors have all been fetched
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ purb = NULL;
+ dev_mgr_start_select_driver( pdev );
+ }
+ }
+ return;
+ }
+ }
+
+ LBL_OUT:
+ usb_free_mem( purb );
+ purb = NULL;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) != USB_DEV_STATE_ZOMB )
+ {
+ if( pdev->desc_buf )
+ {
+ usb_free_mem( pdev->desc_buf );
+ pdev->desc_buf_size = 0;
+ pdev->desc_buf = NULL;
+ pdev->pusb_dev_desc = NULL;
+ pdev->usb_config = NULL;
+ }
+ }
+ unlock_dev( pdev, TRUE );
+
+ return;
+}
+
+BOOL
+dev_mgr_start_select_driver(
+PUSB_DEV pdev
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_EVENT pevent;
+ BOOL bret;
+
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ KeAcquireSpinLockAtDpcLevel( &dev_mgr->event_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ bret = FALSE;
+ goto LBL_OUT;
+ }
+
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ {
+ bret = FALSE;
+ goto LBL_OUT;
+ }
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->event = USB_EVENT_DEFAULT;
+ pevent->pdev = pdev;
+ pevent->context = 0;
+ pevent->param = 0;
+ pevent->pnext = 0; //vertical queue for serialized operation
+ pevent->process_event = dev_mgr_event_select_driver;
+ pevent->process_queue = event_list_default_process_queue;
+
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ bret = TRUE;
+
+ LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLockFromDpcLevel( &dev_mgr->event_list_lock );
+ return bret;
+}
+
+BOOL
+dev_mgr_connect_to_dev(
+PVOID Parameter
+)
+{
+ PUSB_DEV pdev;
+ DEV_HANDLE dev_handle;
+ NTSTATUS status;
+ PUSB_DRIVER pdriver;
+ PCONNECT_DATA pcd = ( PCONNECT_DATA )Parameter;
+ PUSB_DEV_MANAGER dev_mgr;
+ CONNECT_DATA param;
+
+ USE_IRQL;
+
+ if( pcd == NULL )
+ return FALSE;
+ dev_handle = pcd->dev_handle;
+ pdriver = pcd->pdriver;
+ dev_mgr = pcd->dev_mgr;
+
+ param.dev_mgr = dev_mgr;
+ param.pdriver = pdriver;
+ param.dev_handle = 0; //not used
+
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ return FALSE;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "dev_mgr_connect_to_dev(): about to call driver's dev_connect\n" ) );
+ status = pdriver->disp_tbl.dev_connect( ¶m, dev_handle );
+ usb_unlock_dev( pdev );
+ return status;
+}
+
+VOID
+dev_mgr_event_select_driver(
+PUSB_DEV pdev,
+ULONG event,
+ULONG context,
+ULONG param
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_DRIVER pdriver, pcand;
+ LONG credit, match, i;
+ DEV_HANDLE handle;
+ CONNECT_DATA cd;
+
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return;
+ }
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ pcand = NULL;
+ match = 0;
+ for( i = HUB_DRIVER_IDX; i < DEVMGR_MAX_DRIVERS; i++ )
+ {
+ //bypass root-hub driver with idx zero
+ pdriver = ( PUSB_DRIVER ) &dev_mgr->driver_list[ i ];
+
+ if( pdriver->driver_desc.flags & USB_DRIVER_FLAG_DEV_CAPABLE )
+ credit = dev_mgr_score_driver_for_dev( dev_mgr, pdriver, pdev->pusb_dev_desc );
+ else
+ {
+ continue;
+ }
+ if( credit > match )
+ pcand = pdriver, match = credit;
+
+ }
+
+ if( match )
+ {
+ // we set class driver here
+ // pdev->dev_driver = pcand;
+ handle = usb_make_handle( pdev->dev_id, 0, 0 );
+ }
+ unlock_dev( pdev, FALSE );
+
+ if( match )
+ {
+
+ cd.dev_handle = handle;
+ cd.pdriver = pcand;
+ cd.dev_mgr = dev_mgr;
+
+ if( dev_mgr_connect_to_dev( &cd ) )
+ return;
+
+ // ExInitializeWorkItem( pwork_item, dev_mgr_connect_to_dev, ( PVOID )pcd );
+ // ExQueueWorkItem( pwork_item, DelayedWorkQueue );
+ }
+ cd.dev_handle = handle;
+ cd.pdriver = &dev_mgr->driver_list[ GEN_DRIVER_IDX ];
+ cd.dev_mgr = dev_mgr;
+ dev_mgr_connect_to_dev( &cd );
+ return;
+}
+
+BOOL
+dev_mgr_build_usb_endp(
+PUSB_INTERFACE pif,
+PUSB_ENDPOINT pendp,
+PUSB_ENDPOINT_DESC pendp_desc
+)
+{
+ if( pendp == NULL || pif == NULL || pendp_desc == NULL )
+ return FALSE;
+
+ pendp->flags = 0;
+ InitializeListHead( &pendp->urb_list ); //pending urb queue
+ pendp->pusb_if = pif;
+ pendp->pusb_endp_desc = pendp_desc;
+ return TRUE;
+}
+
+BOOL
+dev_mgr_build_usb_if(
+PUSB_CONFIGURATION pcfg,
+PUSB_INTERFACE pif,
+PUSB_INTERFACE_DESC pif_desc,
+BOOL alt_if
+)
+{
+ LONG i;
+ PUSB_ENDPOINT_DESC pendp_desc;
+
+ if( pcfg == NULL || pif == NULL || pif_desc == NULL )
+ return FALSE;
+
+ if( alt_if == FALSE )
+ {
+ pif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF
+ ? MAX_ENDPS_PER_IF
+ : pif_desc->bNumEndpoints;
+
+ pif->pif_drv = NULL;
+ pif->pusb_config = pcfg;
+ pif->pusb_if_desc = pif_desc;
+ pif->if_ext_size = 0;
+ pif->if_ext = NULL;
+
+ InitializeListHead( &pif->altif_list );
+ pif->altif_count = 0;
+
+ pendp_desc = ( PUSB_ENDPOINT_DESC )( &( ( PBYTE )pif_desc )[ sizeof( USB_INTERFACE_DESC ) ] );
+
+ for( i = 0; i < pif->endp_count; i++, pendp_desc++ )
+ {
+ dev_mgr_build_usb_endp( pif, &pif->endp[ i ], pendp_desc );
+ }
+ }
+ else
+ {
+ PUSB_INTERFACE paltif;
+ PLIST_ENTRY pthis, pnext;
+
+ pif->altif_count++;
+ paltif = usb_alloc_mem( NonPagedPool, sizeof( USB_INTERFACE ) );
+ RtlZeroMemory( paltif, sizeof( USB_INTERFACE ) );
+ InsertTailList( &pif->altif_list, &paltif->altif_list );
+ paltif->pif_drv = NULL;
+ paltif->pusb_config = pcfg;
+ paltif->pusb_if_desc = pif_desc;
+ paltif->if_ext_size = 0;
+ paltif->if_ext = NULL;
+ paltif->endp_count = pif_desc->bNumEndpoints > MAX_ENDPS_PER_IF
+ ? MAX_ENDPS_PER_IF
+ : pif_desc->bNumEndpoints;
+
+ ListFirst( &pif->altif_list, pthis );
+
+ while( pthis )
+ {
+ //synchronize the altif_count;
+ PUSB_INTERFACE pthis_if;
+ pthis_if =( PUSB_INTERFACE )( ( ( PBYTE )pthis ) - offsetof( USB_INTERFACE, altif_list ) );
+ pthis_if->altif_count = pif->altif_count;
+ ListNext( &pif->altif_list, pthis, pnext );
+ }
+
+ }
+ return TRUE;
+}
+
+NTSTATUS
+dev_mgr_build_usb_config(
+PUSB_DEV pdev,
+PBYTE pbuf,
+ULONG config_val,
+LONG config_count
+)
+{
+ PUSB_CONFIGURATION pcfg;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_INTERFACE pif;
+ int i;
+ LONG if_count;
+
+ if( pdev == NULL || pbuf == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+
+ pdev->usb_config = usb_alloc_mem( NonPagedPool, sizeof( USB_CONFIGURATION ) );
+ pcfg = pdev->usb_config;
+
+ if( pdev->usb_config == NULL )
+ return STATUS_NO_MEMORY;
+
+ RtlZeroMemory( pcfg, sizeof( USB_CONFIGURATION ) );
+ pcfg->pusb_config_desc = usb_find_config_desc_by_val( pbuf,
+ config_val,
+ config_count );
+
+ if( pcfg->pusb_config_desc == NULL )
+ {
+ usb_free_mem( pcfg );
+ pdev->usb_config = NULL;
+ return STATUS_UNSUCCESSFUL;
+ }
+ pcfg->if_count = pcfg->pusb_config_desc->bNumInterfaces;
+ pcfg->pusb_dev = pdev;
+ pif_desc = ( PUSB_INTERFACE_DESC )&( ( PBYTE )pcfg->pusb_config_desc )[ sizeof( USB_CONFIGURATION_DESC ) ];
+ if_count = pcfg->if_count;
+
+ for( i = 0 ; i < if_count; i++, pif_desc++ )
+ {
+ if( pif_desc->bAlternateSetting == 0)
+ {
+ dev_mgr_build_usb_if( pcfg, &pcfg->interf[ i ], pif_desc, FALSE );
+ }
+ else
+ {
+ i--;
+ pif = &pcfg->interf[ i ];
+ dev_mgr_build_usb_if( pcfg, pif, pif_desc, TRUE );
+ }
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+dev_mgr_destroy_usb_config(
+PUSB_CONFIGURATION pcfg
+)
+{
+ long i;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_INTERFACE pif;
+
+ if( pcfg == NULL )
+ return FALSE;
+
+ for( i = 0; i < pcfg->if_count; i++ )
+ {
+ pif = &pcfg->interf[ i ];
+
+ if( pif->altif_count )
+ {
+ ListFirst( &pif->altif_list, pthis );
+ while( pthis )
+ {
+ PUSB_INTERFACE pthis_if;
+ pthis_if =( PUSB_INTERFACE )( ( ( PBYTE )pthis ) - offsetof( USB_INTERFACE, altif_list ) );
+ RemoveEntryList( pthis );
+ usb_free_mem( pthis_if );
+ if( IsListEmpty( &pif->altif_list ) == TRUE )
+ break;
+
+ ListFirst( &pif->altif_list, pthis );
+ }
+ }
+ }
+ usb_free_mem( pcfg );
+ return TRUE;
+}
+
+#define is_dev_product_match( pdriVER, pdev_DESC ) \
+( ( pdriVER )->driver_desc.vendor_id == ( pdev_DESC )->idVendor \
+ && ( pdriVER )->driver_desc.product_id == ( pdev_DESC )->idProduct )
+
+LONG
+dev_mgr_score_driver_for_dev(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver,
+PUSB_DEVICE_DESC pdev_desc
+)
+{
+ LONG credit = 0;
+
+ //assume supports all the sub_class are supported if sub_class is zero
+ if( pdriver->driver_desc.dev_class == pdev_desc->bDeviceClass )
+ {
+ if( pdriver->driver_desc.dev_sub_class == 0 && pdriver->driver_desc.dev_protocol == 0 )
+ credit = 3;
+ else if( pdriver->driver_desc.dev_sub_class == pdev_desc->bDeviceSubClass )
+ {
+ if( pdriver->driver_desc.dev_protocol == 0 )
+ credit = 6;
+ else if( pdriver->driver_desc.dev_protocol == pdev_desc->bDeviceProtocol )
+ credit = 9;
+ }
+ }
+
+ if( is_dev_product_match( pdriver, pdev_desc ) )
+ credit += 20;
+
+ return credit;
+}
+
+LONG
+dev_mgr_score_driver_for_if(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver,
+PUSB_INTERFACE_DESC pif_desc
+)
+{
+ LONG credit;
+
+ if( pdriver == NULL
+ || !( pdriver->driver_desc.flags & USB_DRIVER_FLAG_IF_CAPABLE )
+ || pif_desc == NULL
+ || dev_mgr == NULL )
+ return 0;
+
+ if( is_header_match( ( PBYTE )pif_desc, USB_DT_INTERFACE ) == FALSE )
+ {
+ return 0;
+ }
+
+ credit = 0;
+ if( ( pdriver->driver_desc.if_class == pif_desc->bInterfaceClass ) )
+ {
+ if( pdriver->driver_desc.if_sub_class == 0 && pdriver->driver_desc.if_protocol == 0 )
+ credit = 2;
+ if( pdriver->driver_desc.if_sub_class == pif_desc->bInterfaceSubClass )
+ {
+ if( pdriver->driver_desc.if_protocol == 0 )
+ credit = 4;
+ if( pdriver->driver_desc.if_protocol == pif_desc->bInterfaceProtocol )
+ credit = 6;
+ }
+ }
+ else
+ credit = 1;
+
+ return credit;
+}
+
+#define is_equal_driver( pd1, pd2, ret ) \
+{\
+ int i;\
+ ret = TRUE;\
+ PUSB_DRIVER pdr1, pdr2;\
+ pdr1 = ( PUSB_DRIVER )( pd1 );\
+ pdr2 = ( PUSB_DRIVER ) ( pd2 );\
+ for( i = 0; i < 16; i++ )\
+ {\
+ if( pdr1->driver_name[ i ] != pdr2->driver_name[ i ] )\
+ {\
+ ret = FALSE;\
+ break;\
+ }\
+ }\
+}
+
+UCHAR
+dev_mgr_register_hcd(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+)
+//return value is the hcd id
+{
+ if( dev_mgr == NULL || hcd == NULL )
+ return 0xff;
+
+ if( dev_mgr->hcd_count >= MAX_HCDS )
+ return 0xff;
+
+ dev_mgr->hcd_array[ dev_mgr->hcd_count++ ] = hcd;
+ return dev_mgr->hcd_count - 1;
+}
+
+BOOL
+dev_mgr_register_irp(
+PUSB_DEV_MANAGER dev_mgr,
+PIRP pirp,
+PURB purb
+)
+{
+ KIRQL old_irql;
+ if( dev_mgr == NULL )
+ return FALSE;
+
+ if( add_irp_to_list( &dev_mgr->irp_list, pirp, purb ) )
+ {
+ return TRUE;
+ }
+ TRAP();
+ return FALSE;
+}
+
+PURB
+dev_mgr_remove_irp(
+PUSB_DEV_MANAGER dev_mgr,
+PIRP pirp
+)
+//caller must guarantee that when this func is called,
+//the urb associated must exist.
+{
+ PURB purb;
+ KIRQL old_irql;
+ if( dev_mgr == NULL )
+ return NULL;
+
+ purb = remove_irp_from_list( &dev_mgr->irp_list, pirp, NULL );
+ return purb;
+}
+
+VOID
+dev_mgr_cancel_irp(
+PDEVICE_OBJECT dev_obj,
+PIRP pirp
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PDEVEXT_HEADER pdev_ext_hdr;
+ ULONG i;
+
+ pdev_ext_hdr = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+ dev_mgr = pdev_ext_hdr->dev_mgr;
+
+ if( dev_obj->CurrentIrp == pirp )
+ {
+ IoReleaseCancelSpinLock( pirp->CancelIrql );
+ // we did not IoStartNextPacket, leave it for the urb completion
+ }
+ else
+ {
+ KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry );
+ IoReleaseCancelSpinLock( pirp->CancelIrql );
+
+ pirp->IoStatus.Information = 0;
+ pirp->IoStatus.Status = STATUS_CANCELLED;
+ IoCompleteRequest( pirp, IO_NO_INCREMENT );
+ // the device queue is moved on, no need to call IoStartNextPacket
+ return;
+ }
+
+ //
+ // remove the irp and call the dev_mgr_cancel_irp
+ // the completion will be done in urb completion
+ //
+ remove_irp_from_list( &dev_mgr->irp_list, pirp, dev_mgr );
+ return;
+
+}
+
+VOID
+dev_mgr_release_hcd(
+PUSB_DEV_MANAGER dev_mgr
+)
+// release the hcd
+{
+ LONG i;
+ PHCD hcd;
+ for( i = 0; i < dev_mgr->hcd_count; i++ )
+ {
+ hcd = dev_mgr->hcd_array[ i ];
+ hcd->hcd_release( hcd );
+ dev_mgr->hcd_array[ i ] = 0;
+ }
+ dev_mgr->hcd_count = 0;
+ return;
+}
+
+VOID
+dev_mgr_start_hcd(
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ LONG i;
+ PHCD hcd;
+ for( i = 0; i < dev_mgr->hcd_count; i++ )
+ {
+ hcd = dev_mgr->hcd_array[ i ];
+ hcd->hcd_start( hcd );
+ }
+ return;
+}
+
+BOOL
+hub_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+)
+{
+ URB urb, *purb;
+ CHAR buf[ 512 ];
+ DEV_HANDLE endp_handle;
+ USB_DEVICE_DESC dev_desc;
+ PUSB_CONFIGURATION_DESC pcfg_desc;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ LONG i, j, found, cfg_val;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DEV pdev;
+ USE_IRQL;
+
+
+ if( param == NULL || dev_handle == 0 )
+ return FALSE;
+
+ dev_mgr = param->dev_mgr;
+
+ pcfg_desc = ( PUSB_CONFIGURATION_DESC ) buf;
+ endp_handle = dev_handle | 0xffff;
+ UsbBuildGetDescriptorRequest(&urb,
+ endp_handle,
+ USB_DT_DEVICE,
+ 0,
+ 0,
+ ( &dev_desc ),
+ ( sizeof( USB_DEVICE_DESC ) ),
+ NULL,
+ 0,
+ 0 );
+
+ status = usb_submit_urb( dev_mgr, &urb );
+ if( status != STATUS_SUCCESS )
+ return FALSE;
+
+ found = FALSE;
+ for( i = 0; i < dev_desc.bNumConfigurations; i++ )
+ {
+ UsbBuildGetDescriptorRequest(&urb,
+ endp_handle,
+ USB_DT_CONFIG,
+ ( USHORT )i,
+ 0,
+ buf,
+ 512,
+ NULL,
+ 0,
+ 0 );
+
+ status = usb_submit_urb( dev_mgr, &urb );
+ if( status != STATUS_SUCCESS )
+ {
+ return FALSE;
+ }
+
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ return FALSE;
+
+ pif_desc = ( PUSB_INTERFACE_DESC )&buf[ sizeof( USB_CONFIGURATION_DESC ) ];
+ for( j = 0; j < pcfg_desc->bNumInterfaces; j++ )
+ {
+ if( pif_desc->bInterfaceClass == USB_CLASS_HUB
+ && pif_desc->bInterfaceSubClass == 0
+ && pif_desc->bNumEndpoints == 1 )
+ {
+ if( ( pif_desc->bInterfaceProtocol > 0 && pif_desc->bInterfaceProtocol < 3 )
+ || ( pif_desc->bInterfaceProtocol == 0 && pdev->flags & USB_DEV_FLAG_HIGH_SPEED )
+ || ( pif_desc->bInterfaceProtocol == 0 && !usb2( pdev->hcd ) ) )
+ {
+ found = TRUE;
+ cfg_val = pcfg_desc->bConfigurationValue;
+ break;
+ }
+ }
+ if( usb_skip_if_and_altif( ( PBYTE* )&pif_desc ) == FALSE )
+ {
+ break;
+ }
+ }
+ usb_unlock_dev( pdev );
+
+ if( found )
+ break;
+
+ if( usb_skip_one_config( ( PBYTE* )&pcfg_desc ) == FALSE )
+ {
+ break;
+ }
+
+ }
+ if( found )
+ {
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return FALSE;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+
+ purb->endp_handle = endp_handle;
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->completion = hub_set_cfg_completion;
+ purb->context = dev_mgr;
+ purb->reference = ( LONG )param->pdriver;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_CONFIGURATION;
+ psetup->wValue = ( USHORT )cfg_val;
+ psetup->wIndex = 0;
+ psetup->wLength = 0;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+hub_set_interface_completion(
+PURB purb,
+PVOID pcontext
+);
+
+void
+hub_set_cfg_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DRIVER pdriver;
+ ULONG endp_handle, dev_handle;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ UCHAR if_idx, if_alt_idx;
+ PUSB_DEV pdev;
+ PUSB_INTERFACE pif;
+ BOOL high_speed, multiple_tt;
+ NTSTATUS status;
+ USE_IRQL;
+
+ if( purb == NULL || pcontext == NULL )
+ return;
+
+ //pdev = NULL;
+ dev_mgr = ( PUSB_DEV_MANAGER )pcontext;
+ endp_handle = purb->endp_handle;
+ dev_handle = endp_handle & 0xffff0000;
+ pdriver = ( PUSB_DRIVER )purb->reference;
+ high_speed = FALSE;
+ multiple_tt = FALSE;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ goto LBL_ERROR;
+ }
+
+ status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ goto LBL_ERROR;
+ }
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ usb_unlock_dev( pdev );
+ goto LBL_ERROR;
+ }
+ if( pdev->flags & USB_DEV_FLAG_HIGH_SPEED )
+ {
+ high_speed = TRUE;
+ hub_if_from_dev( pdev, pif );
+ if( pif->altif_count )
+ {
+ multiple_tt = TRUE;
+ if_idx = pif - &pdev->usb_config->interf[ 0 ];
+ }
+ }
+ unlock_dev( pdev, TRUE );
+ usb_unlock_dev( pdev );
+
+ if( !high_speed || !multiple_tt )
+ {
+ hub_set_interface_completion( purb, pcontext );
+ return;
+ }
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+
+ // set the mult-tt if exist
+ purb->endp_handle = endp_handle;
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->completion = hub_set_interface_completion;
+ purb->context = dev_mgr;
+ purb->reference = ( LONG )pdriver;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_INTERFACE;
+ psetup->wValue = ( USHORT )1; // alternate tt
+ psetup->wIndex = if_idx; // if index
+ psetup->wLength = 0;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ return;
+
+LBL_ERROR:
+ usb_free_mem( purb );
+ purb = NULL;
+ return;
+}
+
+void
+hub_set_interface_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+
+ PUSB_ENDPOINT pendp;
+ NTSTATUS status;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_DEV_MANAGER dev_mgr;
+ PBYTE dev_ext;
+ DEV_HANDLE endp_handle;
+ PUSB_DRIVER pdriver;
+
+ if( purb == NULL || pcontext == NULL )
+ return;
+
+ //pdev = NULL;
+ dev_mgr = ( PUSB_DEV_MANAGER )pcontext;
+ endp_handle = purb->endp_handle;
+ pdriver = ( PUSB_DRIVER )purb->reference;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ dev_ext = usb_alloc_mem( NonPagedPool, sizeof( HUB2_EXTENSION ) );
+ if( dev_ext == NULL )
+ {
+ goto LBL_OUT;
+ }
+
+ //
+ //acquire hub descriptor
+ //
+ RtlZeroMemory( dev_ext, sizeof( HUB2_EXTENSION ) );
+ urb_init( purb );
+
+ purb->data_buffer = ( PUCHAR )&( ( HUB2_EXTENSION* ) dev_ext )->hub_desc;
+ purb->endp_handle = endp_handle;
+ purb->data_length = sizeof( USB_HUB_DESCRIPTOR );
+ purb->completion = hub_get_hub_desc_completion;
+ purb->context = ( PVOID )dev_mgr;
+ purb->reference = ( ULONG )dev_ext;
+ purb->pirp = ( PIRP )pdriver;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet;
+ psetup->bmRequestType = 0xa0;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = ( 0x29 << 8 );
+ psetup->wLength = sizeof( USB_HUB_DESCRIPTOR );
+ status = usb_submit_urb( dev_mgr, purb );
+
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( dev_ext );
+ goto LBL_OUT;
+ }
+ return;
+
+ LBL_OUT:
+
+ //clear the dev_driver fields in the dev.
+ usb_free_mem( purb );
+ return;
+}
+
+
+void
+hub_power_on_port_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DEV pdev;
+ if( purb == NULL )
+ return;
+ if( pcontext == NULL )
+ goto LBL_OUT;
+
+ dev_mgr = ( PUSB_DEV_MANAGER ) pcontext;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_power_on_port_completion(): port%d power on failed\n", purb->reference ) );
+ }
+ else
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "hub_power_on_port_completion(): port%d power on succeed\n", purb->reference ) );
+ }
+
+LBL_OUT:
+ usb_free_mem( purb );
+ return;
+}
+
+NTSTATUS
+hub_power_on_port(
+PUSB_DEV pdev,
+UCHAR port_idx
+)
+{
+
+ PUSB_ENDPOINT pendp;
+ NTSTATUS status;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_DEV_MANAGER dev_mgr;
+ PBYTE dev_ext;
+ DEV_HANDLE endp_handle;
+ PURB purb;
+ PHCD hcd;
+
+ USE_IRQL;
+ if( pdev == NULL || port_idx == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return STATUS_NO_MEMORY;
+
+ urb_init( purb );
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto LBL_OUT;
+ }
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+
+ purb->completion = hub_power_on_port_completion;
+ purb->context = ( PVOID )dev_mgr;
+ purb->reference = ( ULONG )port_idx;
+ purb->pdev = pdev;
+ purb->pendp = &pdev->default_endp;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet;
+ psetup->bmRequestType = 0x23;
+ psetup->bRequest = USB_REQ_SET_FEATURE;
+ psetup->wValue = USB_PORT_FEAT_POWER;
+ psetup->wIndex = ( WORD )port_idx;
+ psetup->wLength = 0;
+
+ unlock_dev( pdev, FALSE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+
+ if( status != STATUS_PENDING )
+ {
+ goto LBL_OUT;
+ }
+ return STATUS_PENDING;
+
+ LBL_OUT:
+
+ usb_free_mem( purb );
+ return status;
+
+}
+
+void
+hub_get_hub_desc_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PHUB2_EXTENSION hub_ext;
+ PUSB_DEV pdev;
+ LONG i;
+ PUSB_INTERFACE pif;
+ ULONG status;
+ LONG port_count;
+ PUSB_DRIVER pdriver;
+ DEV_HANDLE dev_handle;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ {
+ return;
+ }
+
+ dev_mgr = ( PUSB_DEV_MANAGER ) pcontext;
+ hub_ext = ( PHUB2_EXTENSION )purb->reference;
+ pdriver = ( PUSB_DRIVER )purb->pirp;
+ dev_handle = purb->endp_handle & 0xffff0000;
+
+ if( pcontext == NULL || purb->reference == 0 )
+ goto LBL_OUT;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ goto LBL_OUT;
+ }
+
+ // obtain the pointer to the dev
+ status = usb_query_and_lock_dev( dev_mgr, purb->endp_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ usb_unlock_dev( pdev );
+ goto LBL_OUT;
+ }
+ // safe to release the pdev ref since we are in urb completion
+ usb_unlock_dev( pdev );
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ||
+ dev_mgr_set_driver( dev_mgr, dev_handle, pdriver, pdev ) == FALSE )
+ {
+ unlock_dev( pdev, TRUE );
+ goto LBL_OUT;
+ }
+
+ //transit the state to configured
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+
+ pdev->dev_ext = ( PBYTE )hub_ext;
+ pdev->dev_ext_size = sizeof( HUB2_EXTENSION );
+
+ port_count = hub_ext->port_count = hub_ext->hub_desc.bNbrPorts;
+ hub_ext->pdev = pdev;
+ for( i = 0; i < pdev->usb_config->if_count; i++ )
+ {
+ pif = &pdev->usb_config->interf[ i ];
+ if( pif->pusb_if_desc->bInterfaceClass == USB_CLASS_HUB
+ && pif->pusb_if_desc->bInterfaceSubClass == 0
+ && pif->pusb_if_desc->bInterfaceProtocol < 3
+ && pif->pusb_if_desc->bNumEndpoints == 1 )
+ {
+ hub_ext->pif = pif;
+ break;
+ }
+ }
+ for( i = 0; i < MAX_HUB_PORTS + 1; i++ )
+ {
+ psq_init( ( PPORT_STATUS_QUEUE )hub_ext->port_status_queue );
+ }
+
+ hub_ext->multiple_tt = ( pif->pusb_if_desc->bInterfaceProtocol == 2 );
+
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+
+ hub_start_int_request( pdev );
+
+ for( i = 0; i < port_count; i ++ )
+ {
+ hub_power_on_port( pdev, ( UCHAR )( i + 1 ) );
+ }
+ return;
+
+ LBL_OUT:
+ if( purb )
+ usb_free_mem( purb );
+
+ if( hub_ext )
+ usb_free_mem( hub_ext );
+ return;
+}
+
+BOOL
+hub_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ return TRUE;
+}
+
+BOOL
+hub_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ PUSB_DEV pdev;
+ //special use of usb_query and lock dev
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) != STATUS_SUCCESS )
+ {
+ //will never be success, since the dev is already in zomb state
+ //at this point, the dev is valid, ref_count is of none use,
+ //no need to lock it
+ if( pdev )
+ {
+ usb_free_mem( pdev->dev_ext );
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL
+hub_lock_unlock_tt(
+PUSB_DEV pdev,
+UCHAR port_idx,
+UCHAR type,
+BOOL lock
+)
+{
+ PUSB_INTERFACE pif;
+ PHUB2_EXTENSION dev_ext;
+ PULONG pmap;
+
+ USE_IRQL;
+
+ if( pdev == NULL || port_idx > 127 )
+ return FALSE;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return FALSE;
+ }
+
+ dev_ext = hub_ext_from_dev( pdev );
+ if( dev_ext == NULL )
+ {
+ unlock_dev( pdev, FALSE );
+ return FALSE;
+ }
+ if( type == USB_ENDPOINT_XFER_INT || \
+ type == USB_ENDPOINT_XFER_ISOC )
+ {
+ pmap = dev_ext->tt_status_map;
+ }
+ else if( type == USB_ENDPOINT_XFER_BULK || \
+ type == USB_ENDPOINT_XFER_CONTROL )
+ {
+ pmap = dev_ext->tt_bulk_map;
+ }
+
+ if( lock )
+ {
+ if( pmap[ port_idx >> 5 ] & ( 1 << port_idx ) )
+ {
+ unlock_dev( pdev, FALSE );
+ return FALSE;
+ }
+ pmap[ port_idx >> 5 ] |= ( 1 << port_idx );
+ }
+ else
+ {
+ pmap[ port_idx >> 5 ] &= ~( 1 << port_idx );
+ }
+
+ unlock_dev( pdev, FALSE );
+ return TRUE;
+}
+
+BOOL
+hub_lock_tt(
+PUSB_DEV pdev,
+UCHAR port_idx,
+UCHAR type // transfer type
+)
+{
+ return hub_lock_unlock_tt( pdev, port_idx, type, TRUE );
+}
+
+BOOL
+hub_unlock_tt(
+PUSB_DEV pdev,
+UCHAR port_idx,
+UCHAR type
+)
+{
+ return hub_lock_unlock_tt( pdev, port_idx, type, FALSE );
+}
+
+VOID
+hub_clear_tt_buffer_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PURB_HS_PIPE_CONTENT pipe_content;
+ PHUB2_EXTENSION hub_ext;
+ PHCD hcd;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ hub_ext = ( PHUB2_EXTENSION )context;
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ pipe_content = ( PURB_HS_PIPE_CONTENT )&purb->reference;
+ hub_unlock_tt( purb->pdev, ( UCHAR )psetup->wIndex, ( UCHAR )pipe_content->trans_type );
+ usb_free_mem( purb );
+ purb = NULL;
+ hcd = hub_ext->pdev->hcd;
+
+ // let those blocked urbs ( sharing the same tt )have chance to be scheduled
+ if( hcd && usb2( hcd ) )
+ hcd->hcd_submit_urb( hcd, NULL, NULL, NULL );
+
+ return;
+}
+
+BOOL
+hub_clear_tt_buffer(
+PUSB_DEV pdev,
+URB_HS_PIPE_CONTENT pipe_content,
+UCHAR port_idx
+)
+// send CLEAR_TT_BUFFER to the hub
+{
+ PURB purb;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PHUB2_EXTENSION hub_ext;
+ PHCD hcd;
+ NTSTATUS status;
+ USE_IRQL;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ if( pipe_content.speed_high )
+ return FALSE;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return FALSE;
+ }
+
+ hub_ext = hub_ext_from_dev( pdev );
+ if( hub_ext == NULL )
+ {
+ unlock_dev( pdev, FALSE );
+ return FALSE;
+ }
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ RtlZeroMemory( purb, sizeof( URB ) );
+
+ if( purb == NULL )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_NO_MEMORY;
+ }
+
+ purb->flags = 0;
+ purb->status = STATUS_SUCCESS;
+ purb->data_buffer = NULL;
+ purb->data_length = 0; // ( hub_ext->port_count + 7 ) / 8;
+
+ // hub_if_from_dev( pdev, pif );
+ purb->pendp = &pdev->default_endp;
+ purb->pdev = pdev;
+
+ purb->completion = hub_clear_tt_buffer_completion;
+ purb->context = hub_ext;
+ purb->reference = *( ( PLONG )&pipe_content );
+
+ purb->pirp = NULL;
+ hcd = pdev->hcd;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ psetup->bmRequestType = 0x23; //host-device class other recepient
+ psetup->bRequest = HUB_REQ_CLEAR_TT_BUFFER;
+ psetup->wValue = ( USHORT )( ( pipe_content.endp_addr ) | ( pipe_content.dev_addr << 4 )| \
+ ( pipe_content.trans_type << 10 ) | ( pipe_content.trans_dir << 15 ) );
+
+ if( hub_ext->multiple_tt )
+ {
+ psetup->wIndex = ( USHORT )port_idx;
+ }
+ else
+ psetup->wIndex = 1;
+
+ psetup->wLength = 0;
+ unlock_dev( pdev, FALSE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL
+hub_event_clear_tt_buffer(
+PUSB_DEV pdev, //always null. we do not use this param
+ULONG event,
+ULONG context,
+ULONG param
+)
+{
+ hub_clear_tt_buffer( pdev, *( ( PURB_HS_PIPE_CONTENT )&context ), ( UCHAR )param );
+ return TRUE;
+}
+
+VOID
+hub_post_clear_tt_event(
+PUSB_DEV pdev,
+BYTE port_idx,
+ULONG pipe
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_EVENT pevent;
+ USE_IRQL;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ KeAcquireSpinLock( &dev_mgr->event_list_lock, &old_irql );
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ return;
+ }
+ pevent = alloc_event( &dev_mgr->event_pool, 1 );
+ if( pevent == NULL )
+ {
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+ TRAP();
+ return;
+ }
+
+ pevent->event = USB_EVENT_WAIT_RESET_PORT;
+ pevent->pdev = pdev;
+ pevent->context = pipe;
+ pevent->param = port_idx;
+ pevent->flags = USB_EVENT_FLAG_ACTIVE;
+ pevent->process_queue = event_list_default_process_queue;
+ pevent->process_event = hub_event_clear_tt_buffer;
+ pevent->pnext = NULL;
+ InsertTailList( &dev_mgr->event_list, &pevent->event_link );
+
+ unlock_dev( pdev, TRUE );
+ KeReleaseSpinLock( &dev_mgr->event_list_lock, old_irql );
+
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ return;
+}
+
+BOOL
+init_irp_list(
+PIRP_LIST irp_list
+)
+{
+ LONG i;
+ KeInitializeSpinLock( &irp_list->irp_list_lock );
+ InitializeListHead( &irp_list->irp_busy_list );
+ InitializeListHead( &irp_list->irp_free_list );
+ irp_list->irp_list_element_array = usb_alloc_mem( NonPagedPool, sizeof( IRP_LIST_ELEMENT ) * MAX_IRP_LIST_SIZE );
+
+ if( irp_list->irp_list_element_array == NULL )
+ return FALSE;
+
+ RtlZeroMemory( irp_list->irp_list_element_array, sizeof( IRP_LIST_ELEMENT ) * MAX_IRP_LIST_SIZE );
+ for( i = 0; i < MAX_IRP_LIST_SIZE; i++ )
+ {
+ InsertTailList( &irp_list->irp_free_list, &irp_list->irp_list_element_array[ i ].irp_link );
+ }
+ irp_list->irp_free_list_count = MAX_IRP_LIST_SIZE;
+ return TRUE;
+}
+
+VOID
+destroy_irp_list(
+PIRP_LIST irp_list
+)
+{
+ InitializeListHead( &irp_list->irp_busy_list );
+ InitializeListHead( &irp_list->irp_free_list );
+ usb_free_mem( irp_list->irp_list_element_array );
+ irp_list->irp_list_element_array = NULL;
+ irp_list->irp_free_list_count = 0;
+ return;
+}
+
+BOOL
+add_irp_to_list(
+PIRP_LIST irp_list,
+PIRP pirp,
+PURB purb
+)
+{
+ KIRQL old_irql;
+ PIRP_LIST_ELEMENT pile;
+
+ if( irp_list == NULL || pirp == NULL || purb == NULL )
+ return FALSE;
+
+ IoAcquireCancelSpinLock( &old_irql );
+ KeAcquireSpinLockAtDpcLevel( &irp_list->irp_list_lock );
+
+ if( irp_list->irp_free_list_count == 0 )
+ {
+ KeReleaseSpinLockFromDpcLevel( &irp_list->irp_list_lock );
+ IoReleaseCancelSpinLock( old_irql );
+ return FALSE;
+ }
+ pile = ( PIRP_LIST_ELEMENT )RemoveHeadList( &irp_list->irp_free_list );
+
+ pile->pirp = pirp;
+ pile->purb = purb;
+
+ irp_list->irp_free_list_count--;
+ InsertTailList( &irp_list->irp_busy_list, &pile->irp_link );
+ IoSetCancelRoutine( pirp, dev_mgr_cancel_irp );
+
+ KeReleaseSpinLockFromDpcLevel( &irp_list->irp_list_lock );
+ IoReleaseCancelSpinLock( old_irql );
+ return TRUE;
+}
+
+PURB
+remove_irp_from_list(
+PIRP_LIST irp_list,
+PIRP pirp,
+PUSB_DEV_MANAGER dev_mgr //if dev_mgr is not NULL, the urb needs to be canceled
+)
+{
+ PIRP_LIST_ELEMENT pile;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ DEV_HANDLE endp_handle;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( irp_list == NULL || pirp == NULL )
+ return NULL;
+
+ KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql );
+
+ if( irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE )
+ {
+ KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql );
+ return NULL;
+ }
+
+ purb = NULL;
+ ListFirst( &irp_list->irp_busy_list, pthis );
+ while( pthis )
+ {
+ pile = struct_ptr( pthis, IRP_LIST_ELEMENT, irp_link );
+ if( pile->pirp == pirp )
+ {
+ purb = pile->purb;
+ pile->pirp = NULL;
+ pile->purb = NULL;
+ RemoveEntryList( pthis );
+ InsertTailList( &irp_list->irp_free_list, pthis );
+ irp_list->irp_free_list_count++;
+ break;
+ }
+ ListNext( &irp_list->irp_busy_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ if( purb == NULL )
+ {
+ // not found
+ KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql );
+ return NULL;
+ }
+
+ endp_handle = purb->endp_handle;
+ KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql );
+
+ if( dev_mgr )
+ {
+ // indicate we needs to cancel the urb, this condition happens only in cancel routine
+ // we should notice that even the hcd_cancel_urb is called, the irp may not be canceled
+ // if the urb does not exist in any queue of the host controller driver, indicating
+ // it is being processed by dpc. Thus, the dpc will certainly prevent the irp in
+ // completion from being canceled at the same time. On the other hand, if the
+ // hcd_cancel_urb succeeds, it either directly complete the irp or queue the dpc for
+ // irp completion. So, there won't be two simutaneous threads processing the same
+ // irp.
+
+ if( usb_query_and_lock_dev( dev_mgr, endp_handle, &pdev ) != STATUS_SUCCESS )
+ return NULL;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ return NULL;
+ }
+
+ hcd = pdev->hcd;
+ endp_from_handle( pdev, endp_handle, pendp );
+ unlock_dev( pdev, TRUE );
+ hcd->hcd_cancel_urb( hcd, pdev, pendp, purb );
+ usb_unlock_dev( pdev );
+ return NULL;
+ }
+ return purb;
+}
+
+BOOL
+irp_list_empty(
+PIRP_LIST irp_list
+)
+{
+ KIRQL old_irql;
+ BOOL ret;
+ KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql );
+ ret = ( irp_list->irp_free_list_count == MAX_IRP_LIST_SIZE );
+ KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql );
+ return ret;
+}
+
+BOOL
+irp_list_full(
+PIRP_LIST irp_list
+)
+{
+ KIRQL old_irql;
+ BOOL ret;
+ KeAcquireSpinLock( &irp_list->irp_list_lock, &old_irql );
+ ret = ( irp_list->irp_free_list_count == 0 );
+ KeReleaseSpinLock( &irp_list->irp_list_lock, old_irql );
+ return ret;
+}
+
+
+VOID
+zzz()
+{};
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/hub.h b/reactos/drivers/usb/nt4compat/usbdriver/hub.h
new file mode 100644
index 00000000000..45a400a628b
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/hub.h
@@ -0,0 +1,794 @@
+
+#ifndef __HUB_H__
+#define __HUB_H__
+
+#include "td.h"
+//#include "hcd.h"
+#include "ntddk.h"
+
+/*
+ * Hub Class feature numbers
+ */
+#define C_HUB_LOCAL_POWER 0
+#define C_HUB_OVER_CURRENT 1
+
+/*
+ * Port feature numbers
+ */
+#define USB_PORT_FEAT_CONNECTION 0
+#define USB_PORT_FEAT_ENABLE 1
+#define USB_PORT_FEAT_SUSPEND 2
+#define USB_PORT_FEAT_OVER_CURRENT 3
+#define USB_PORT_FEAT_RESET 4
+#define USB_PORT_FEAT_POWER 8
+#define USB_PORT_FEAT_LOWSPEED 9
+#define USB_PORT_FEAT_C_CONNECTION 16
+#define USB_PORT_FEAT_C_ENABLE 17
+#define USB_PORT_FEAT_C_SUSPEND 18
+#define USB_PORT_FEAT_C_OVER_CURRENT 19
+#define USB_PORT_FEAT_C_RESET 20
+
+/* wPortStatus bits */
+#define USB_PORT_STAT_CONNECTION 0x0001
+#define USB_PORT_STAT_ENABLE 0x0002
+#define USB_PORT_STAT_SUSPEND 0x0004
+#define USB_PORT_STAT_OVERCURRENT 0x0008
+#define USB_PORT_STAT_RESET 0x0010
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+
+/* usb 2.0 features */
+#define USB_PORT_STAT_HIGH_SPEED 0x0400
+#define USB_PORT_STAT_PORT_TEST 0x0800
+#define USB_PORT_STAT_PORT_INDICATOR 0x1000
+
+/* wPortChange bits */
+#define USB_PORT_STAT_C_CONNECTION 0x0001
+#define USB_PORT_STAT_C_ENABLE 0x0002
+#define USB_PORT_STAT_C_SUSPEND 0x0004
+#define USB_PORT_STAT_C_OVERCURRENT 0x0008
+#define USB_PORT_STAT_C_RESET 0x0010
+
+/* wHubCharacteristics (masks) */
+#define HUB_CHAR_LPSM 0x0003
+#define HUB_CHAR_COMPOUND 0x0004
+#define HUB_CHAR_OCPM 0x0018
+
+/*
+ *Hub Status & Hub Change bit masks
+ */
+#define HUB_STATUS_LOCAL_POWER 0x0001
+#define HUB_STATUS_OVERCURRENT 0x0002
+
+#define HUB_CHANGE_LOCAL_POWER 0x0001
+#define HUB_CHANGE_OVERCURRENT 0x0002
+
+#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */
+
+//event definitions
+
+#define MAX_EVENTS 128
+#define MAX_TIMER_SVCS 24
+
+#define USB_EVENT_FLAG_ACTIVE 0x80000000
+
+#define USB_EVENT_FLAG_QUE_TYPE 0x000000FF
+#define USB_EVENT_FLAG_QUE_RESET 0x01
+#define USB_EVENT_FLAG_NOQUE 0x00
+
+#define USB_EVENT_DEFAULT 0x00 //as a placeholder
+#define USB_EVENT_INIT_DEV_MGR 0x01
+#define USB_EVENT_HUB_POLL 0x02
+#define USB_EVENT_WAIT_RESET_PORT 0x03
+#define USB_EVENT_CLEAR_TT_BUFFER 0x04
+
+typedef VOID ( *PROCESS_QUEUE )(
+PLIST_HEAD event_list,
+struct _USB_EVENT_POOL *event_pool,
+struct _USB_EVENT *usb_event,
+struct _USB_EVENT *out_event
+);
+
+typedef VOID ( *PROCESS_EVENT )(
+PUSB_DEV dev,
+ULONG event,
+ULONG context,
+ULONG param
+);
+
+typedef struct _USB_EVENT
+{
+ LIST_ENTRY event_link;
+ ULONG flags;
+ ULONG event;
+ PUSB_DEV pdev;
+ ULONG context;
+ ULONG param;
+ struct _USB_EVENT *pnext; //vertical queue for serialized operation
+ PROCESS_EVENT process_event;
+ PROCESS_QUEUE process_queue;
+
+} USB_EVENT, *PUSB_EVENT;
+
+typedef struct _USB_EVENT_POOL
+{
+ PUSB_EVENT event_array;
+ LIST_HEAD free_que;
+ LONG free_count;
+ LONG total_count;
+ KSPIN_LOCK pool_lock;
+
+} USB_EVENT_POOL, *PUSB_EVENT_POOL;
+
+BOOL
+init_event_pool(
+PUSB_EVENT_POOL pool
+);
+
+BOOL
+free_event(
+PUSB_EVENT_POOL pool,
+PUSB_EVENT pevent
+); //add qhs till pnext == NULL
+
+PUSB_EVENT
+alloc_event(
+PUSB_EVENT_POOL pool,
+LONG count
+); //null if failed
+
+BOOL
+destroy_event_pool(
+PUSB_EVENT_POOL pool
+);
+
+VOID
+lock_event_pool(
+PUSB_EVENT_POOL pool
+);
+
+VOID
+unlock_event_pool(
+PUSB_EVENT_POOL pool
+);
+
+#define DEV_MGR_TIMER_INTERVAL_NS ( 10 * 1000 * 10 ) //unit 100 ns
+#define DEV_MGR_TIMER_INTERVAL_MS 10
+
+typedef VOID ( *TIMER_SVC_HANDLER )(PUSB_DEV dev, PVOID context);
+
+typedef struct _TIMER_SVC
+{
+ LIST_ENTRY timer_svc_link;
+ ULONG counter;
+ ULONG threshold;
+ ULONG context;
+ PUSB_DEV pdev;
+ TIMER_SVC_HANDLER func;
+
+} TIMER_SVC, *PTIMER_SVC;
+
+typedef struct _TIMER_SVC_POOL
+{
+ PTIMER_SVC timer_svc_array;
+ LIST_HEAD free_que;
+ LONG free_count;
+ LONG total_count;
+ KSPIN_LOCK pool_lock;
+
+} TIMER_SVC_POOL, *PTIMER_SVC_POOL;
+
+BOOL
+init_timer_svc_pool(
+PTIMER_SVC_POOL pool
+);
+BOOL
+free_timer_svc(
+PTIMER_SVC_POOL pool,
+PTIMER_SVC ptimer
+);
+
+PTIMER_SVC
+alloc_timer_svc(
+PTIMER_SVC_POOL pool,
+LONG count
+); //null if failed
+
+BOOL
+destroy_timer_svc_pool(
+PTIMER_SVC_POOL pool
+);
+
+VOID
+lock_timer_svc_pool(
+PTIMER_SVC_POOL pool
+);
+
+VOID
+unlock_timer_svc_pool(
+PTIMER_SVC_POOL pool
+);
+
+#define MAX_IRP_LIST_SIZE 32
+
+typedef struct _IRP_LIST_ELEMENT
+{
+ LIST_ENTRY irp_link;
+ PIRP pirp;
+ struct _URB *purb;
+
+} IRP_LIST_ELEMENT, *PIRP_LIST_ELEMENT;
+
+typedef struct _IRP_LIST
+{
+ KSPIN_LOCK irp_list_lock;
+ LIST_HEAD irp_busy_list;
+ LONG irp_free_list_count;
+ LIST_HEAD irp_free_list;
+ PIRP_LIST_ELEMENT irp_list_element_array;
+
+} IRP_LIST, *PIRP_LIST;
+
+BOOL
+init_irp_list(
+PIRP_LIST irp_list
+);
+
+VOID
+destroy_irp_list(
+PIRP_LIST irp_list
+);
+
+BOOL
+add_irp_to_list(
+PIRP_LIST irp_list,
+PIRP pirp,
+PURB purb
+);
+
+PURB
+remove_irp_from_list(
+PIRP_LIST irp_list,
+PIRP pirp,
+struct _USB_DEV_MANAGER *dev_mgr
+);
+
+BOOL
+irp_list_empty(
+PIRP_LIST irp_list
+);
+
+BOOL
+irp_list_full(
+PIRP_LIST irp_list
+);
+
+#define MAX_HUB_PORTS 8
+#define USB_HUB_INTERVAL 0xff
+
+#define USB_PORT_FLAG_STATE_MASK ( 0xf << 16 )
+#define USB_PORT_FLAG_ENABLE ( 0x1 << 16 )
+#define USB_PORT_FLAG_DISABLE ( 0x2 << 16 )
+#define USB_PORT_FLAG_DISCONNECT ( 0x3 << 16 )
+
+#define USB_PORT_QUE_STATE_MASK 0xff // for detail, refer to document.txt
+#define STATE_IDLE 0x00
+#define STATE_EXAMINE_STATUS_QUE 0x01 // set when post a event to examine the status queue
+#define STATE_WAIT_STABLE 0x02 // set when a new connection comes and about to wait some ms
+#define STATE_WAIT_RESET 0x03 // set when dev stable and about to reset, may pending in the event queue
+#define STATE_WAIT_RESET_COMPLETE 0x04 // set when reset signal is about to assert on the port
+#define STATE_WAIT_ADDRESSED 0x05 // set when reset complete and before address is assigned
+
+#define port_state( port_FLAG ) \
+( port_FLAG & USB_PORT_QUE_STATE_MASK )
+
+#define set_port_state( port_FLAG, stATE ) \
+{ port_FLAG = ( port_FLAG & (~USB_PORT_QUE_STATE_MASK ) ) | ( stATE & USB_PORT_QUE_STATE_MASK ) ;}
+
+#define default_endp_handle( endp_HANDLE ) \
+( ( endp_HANDLE & 0xffff ) == 0xffff )
+
+#define is_if_dev( pdev ) ( pdev->flags & USB_DEV_FLAG_IF_DEV )
+
+#define is_composite_dev( pdev ) \
+( is_if_dev( pdev ) == FALSE \
+&& pdev->pusb_dev_desc->bDeviceClass == 0 \
+&& pdev->pusb_dev_desc->bDeviceSubClass == 0 )
+
+#define dev_handle_from_dev( pdev ) ( pdev->dev_id << 16 )
+
+#pragma pack( push, hub_align, 1 )
+typedef struct _USB_PORT_STATUS
+{
+ USHORT wPortStatus;
+ USHORT wPortChange;
+
+} USB_PORT_STATUS, *PUSB_PORT_STATUS;
+
+
+typedef struct _USB_HUB_STATUS
+{
+ USHORT wHubStatus;
+ USHORT wHubChange;
+
+} USB_HUB_STATUS, *PUSB_HUB_STATUS;
+
+
+typedef struct _USB_HUB_DESCRIPTOR
+{
+ BYTE bLength;
+ BYTE bDescriptorType;
+ BYTE bNbrPorts;
+ USHORT wHubCharacteristics;
+ BYTE bPwrOn2PwrGood;
+ BYTE bHubContrCurrent;
+
+ /* DeviceRemovable and PortPwrCtrlMask want to be variable-length
+ bitmaps that hold max 256 entries, but for now they're ignored */
+ BYTE bitmap[0];
+} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
+#pragma pack( pop, hub_align )
+
+typedef struct _PORT_STATUS_QUEUE
+{
+ USB_PORT_STATUS port_status[ 4 ];
+ BYTE status_count;
+ ULONG port_flags;
+
+} PORT_STATUS_QUEUE, *PPORT_STATUS_QUEUE;
+
+VOID
+psq_init(
+PPORT_STATUS_QUEUE psq
+);
+
+BOOL
+psq_enqueue(
+PPORT_STATUS_QUEUE psq,
+ULONG status
+);
+ULONG
+psq_outqueue(
+PPORT_STATUS_QUEUE psq
+); //return 0xffffffff if no element
+
+BOOL
+psq_push(
+PPORT_STATUS_QUEUE psq,
+ULONG status
+);
+
+#define psq_is_empty( pSQ ) ( ( pSQ )->status_count == 0 )
+#define psq_is_full( pSQ ) ( ( pSQ )->status_count == 4 )
+#define psq_count( psq ) ( ( psq )->status_count )
+#define psq_peek( psq, i ) \
+( ( ( ULONG )i ) < 3 ? ( psq )->port_status[ i ] : ( psq )->port_status[ 3 ] )
+
+#define MAX_DEVS 127
+#define EHCI_MAX_ROOT_PORTS 15
+
+typedef struct _HUB_EXTENSION
+{
+
+ LONG port_count;
+ PUSB_DEV child_dev[ MAX_HUB_PORTS + 1 ];
+
+ PORT_STATUS_QUEUE port_status_queue[ MAX_HUB_PORTS + 1];
+ PUSB_DEV pdev;
+ PUSB_INTERFACE pif;
+ BYTE int_data_buf[ 64 ];
+
+ USB_HUB_STATUS hub_status;
+ USB_PORT_STATUS port_status; //working data buf for get port feature
+
+ USB_PORT_STATUS rh_port1_status; //working buf for get rh port1 feature
+ USB_PORT_STATUS rh_port2_status; //working buf for get rh port2 feature
+
+ USB_HUB_DESCRIPTOR hub_desc;
+
+} HUB_EXTENSION, *PHUB_EXTENSION;
+
+typedef struct _HUB2_PORT_TT
+{
+ ULONG tt_busy : 1;
+
+} HUB2_PORT_TT, *PHUB2_PORT_TT;
+
+typedef struct _HUB2_EXTENSION
+{
+
+ LONG port_count;
+ PUSB_DEV child_dev[ MAX_HUB_PORTS + 1 ];
+
+ PORT_STATUS_QUEUE port_status_queue[ MAX_HUB_PORTS + 1];
+
+ PUSB_DEV pdev;
+ PUSB_INTERFACE pif;
+ BYTE int_data_buf[ 32 ]; // for ports up to 127
+
+ USB_HUB_STATUS hub_status;
+ USB_PORT_STATUS port_status; //working data buf for get port feature
+
+ USB_PORT_STATUS rh_port_status[ EHCI_MAX_ROOT_PORTS ]; //working buf for get rh ports feature
+
+ UCHAR multiple_tt; // boolean
+ ULONG tt_status_map[ 4 ]; // bit map to indicate the indexed tt's periodic buffer busy or not
+ ULONG tt_bulk_map[ 4 ]; // bit map to indicate the indexed tt's bulk/control buffer busy or not
+ USB_HUB_DESCRIPTOR hub_desc;
+
+} HUB2_EXTENSION, *PHUB2_EXTENSION;
+
+typedef struct _CONNECT_DATA
+{
+ DEV_HANDLE dev_handle;
+ struct _USB_DRIVER *pdriver;
+ struct _USB_DEV_MANAGER *dev_mgr;
+
+} CONNECT_DATA, *PCONNECT_DATA;
+
+typedef BOOL ( *PDEV_CONNECT_EX )( PCONNECT_DATA init_param, DEV_HANDLE dev_handle );
+typedef BOOL ( *PDEV_CONNECT )( struct _USB_DEV_MANAGER *dev_mgr, DEV_HANDLE dev_handle );
+typedef BOOL ( *PDRVR_INIT )( struct _USB_DEV_MANAGER *dev_mgr, struct _USB_DRIVER *pdriver );
+
+typedef struct _PNP_DISPATCH
+{
+ ULONG version;
+ PDEV_CONNECT_EX dev_connect;
+ PDEV_CONNECT dev_reserved; //currently we do not use this entry
+ PDEV_CONNECT dev_stop;
+ PDEV_CONNECT dev_disconnect;
+
+}PNP_DISPATCH, *PPNP_DISPATCH;
+
+#define USB_DRIVER_FLAG_IF_CAPABLE 0x80000000
+#define USB_DRIVER_FLAG_DEV_CAPABLE 0x40000000
+
+typedef struct _USB_DRIVER_DESCRIPTION
+{
+ // Device Info
+ DWORD flags;
+ WORD vendor_id; // USB Vendor ID
+ WORD product_id; // USB Product ID.
+ WORD release_num; // Release Number of Device
+
+ // Interface Info
+ BYTE config_val; // Configuration Value
+ BYTE if_num; // Interface Number
+ BYTE if_class; // Interface Class
+ BYTE if_sub_class; // Interface SubClass
+ BYTE if_protocol; // Interface Protocol
+
+ // Driver Info
+ PBYTE driver_name; // Driver name for Name Registry
+ BYTE dev_class; // Device Class (from SampleStorageDeviceID.h)
+ BYTE dev_sub_class; // Device Subclass
+ BYTE dev_protocol; // Protocol Info.
+
+} USB_DRIVER_DESCRIPTION,*PUSB_DRIVER_DESCRIPTION;
+
+#define DEVMGR_MAX_DRIVERS 6
+#define RH_DRIVER_IDX 0
+#define HUB_DRIVER_IDX 1
+#define UMSS_DRIVER_IDX 2
+#define COMP_DRIVER_IDX 3
+#define GEN_DRIVER_IDX 4
+#define GEN_IF_DRIVER_IDX 5
+
+typedef struct _USB_DRIVER
+{
+ USB_DRIVER_DESCRIPTION driver_desc;
+ PNP_DISPATCH disp_tbl;
+
+ PBYTE driver_ext;
+ LONG driver_ext_size;
+
+ PDRVR_INIT driver_init;
+ PDRVR_INIT driver_destroy;
+
+} USB_DRIVER, *PUSB_DRIVER;
+
+extern USB_DRIVER g_driver_list[ DEVMGR_MAX_DRIVERS ];
+
+#define MAX_HCDS 8
+#define dev_mgr_from_hcd( hCD ) ( ( hCD )->hcd_get_dev_mgr( hCD ) )
+#define dev_mgr_from_dev( pdEV ) ( dev_mgr_from_hcd( pdEV->hcd ) )
+
+typedef struct _USB_DEV_MANAGER
+{
+ //BYTE dev_addr_map[ MAX_DEVS / 8 ]; //one bit per dev
+ struct _HCD *hcd_array[ MAX_HCDS ];
+ unsigned char hcd_count;
+
+ KSPIN_LOCK dev_list_lock;
+ LIST_HEAD dev_list;
+
+ //PDEVICE_EXTENSION pdev_ext;
+
+ PVOID pthread;
+ BOOL term_flag;
+ KEVENT wake_up_event;
+
+ KSPIN_LOCK event_list_lock;
+ LIST_HEAD event_list;
+ USB_EVENT_POOL event_pool;
+
+ KTIMER dev_mgr_timer;
+ KDPC dev_mgr_timer_dpc;
+ KSPIN_LOCK timer_svc_list_lock;
+ LIST_HEAD timer_svc_list;
+ TIMER_SVC_POOL timer_svc_pool;
+ LONG timer_click;
+ IRP_LIST irp_list;
+
+ PUSB_DRIVER driver_list;
+ LONG hub_count;
+ LIST_HEAD hub_list; //for reference only
+
+ //statistics
+ LONG conn_count; //will also be used to assign device id
+ PDRIVER_OBJECT usb_driver_obj; //this driver object
+ LONG open_count; //increment when IRP_MJ_CREATE arrives
+ //and decrement when IRP_MJ_CLOSE arrives
+
+} USB_DEV_MANAGER, *PUSB_DEV_MANAGER;
+
+BOOL
+dev_mgr_post_event(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_EVENT event
+);
+
+BOOL
+dev_mgr_init(
+PUSB_DEV dev, //always null. we do not use this param
+ULONG event,
+ULONG dev_mgr
+);
+
+VOID
+dev_mgr_destroy(
+PUSB_DEV_MANAGER dev_mgr
+);
+
+VOID
+dev_mgr_thread(
+PVOID dev_mgr
+);
+
+VOID
+dev_mgr_timer_dpc_callback(
+PKDPC Dpc,
+PVOID DeferredContext,
+PVOID SystemArgument1,
+PVOID SystemArgument2
+);
+
+BOOL
+dev_mgr_request_timer_svc(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev,
+ULONG context,
+ULONG due_time,
+TIMER_SVC_HANDLER handler
+);
+
+BYTE
+dev_mgr_alloc_addr(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+);
+
+BOOL
+dev_mgr_free_addr(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev,
+BYTE addr
+);
+
+PUSB_DEV
+dev_mgr_alloc_device(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+);
+
+VOID
+dev_mgr_free_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev
+);
+
+VOID
+dev_mgr_disconnect_dev(
+PUSB_DEV pdev
+);
+
+BOOL
+dev_mgr_strobe(
+PUSB_DEV_MANAGER dev_mgr
+);
+
+NTSTATUS
+dev_mgr_build_usb_config(
+PUSB_DEV pdev,
+PBYTE pbuf,
+ULONG config_val,
+LONG config_count
+);
+
+UCHAR
+dev_mgr_register_hcd(
+PUSB_DEV_MANAGER dev_mgr,
+PHCD hcd
+);
+
+NTSTATUS
+dev_mgr_dispatch(
+IN PUSB_DEV_MANAGER dev_mgr,
+IN PIRP irp
+);
+
+BOOL
+dev_mgr_register_irp(
+PUSB_DEV_MANAGER dev_mgr,
+PIRP pirp,
+PURB purb
+);
+
+PURB
+dev_mgr_remove_irp(
+PUSB_DEV_MANAGER dev_mgr,
+PIRP pirp
+);
+
+LONG
+dev_mgr_score_driver_for_if(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver,
+PUSB_INTERFACE_DESC pif_desc
+);
+
+BOOL
+dev_mgr_set_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle,
+PUSB_DRIVER pdriver,
+PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle
+);
+
+BOOL
+dev_mgr_set_if_driver(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle,
+PUSB_DRIVER pdriver,
+PUSB_DEV pdev //if pdev != NULL, we use pdev instead if_handle
+);
+
+VOID
+dev_mgr_release_hcd(
+PUSB_DEV_MANAGER dev_mgr
+);
+
+VOID
+dev_mgr_start_hcd(
+PUSB_DEV_MANAGER dev_mgr
+);
+
+VOID
+event_list_default_process_queue(
+PLIST_HEAD event_list,
+PUSB_EVENT_POOL event_pool,
+PUSB_EVENT usb_event,
+PUSB_EVENT out_event
+);
+
+VOID
+event_list_default_process_event(
+PUSB_DEV dev,
+ULONG event,
+ULONG context,
+ULONG param
+);
+
+// root hub routines and definitions
+
+#define RH_INTERVAL ( USB_HUB_INTERVAL / DEV_MGR_TIMER_INTERVAL_MS )
+
+BOOL
+rh_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+rh_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+rh_destroy(
+PUSB_DEV pdev
+);
+
+VOID
+rh_timer_svc(
+PUSB_DEV dev,
+PVOID context
+);
+
+BOOL
+rh_submit_urb(
+PUSB_DEV pdev,
+PURB urb
+);
+
+BOOL
+hub_init(
+PUSB_DEV pdev
+);
+
+BOOL
+hub_destroy(
+PUSB_DEV pdev
+);
+
+BOOL
+hub_lock_tt(
+PUSB_DEV pdev,
+UCHAR port_idx,
+UCHAR type // transfer type
+);
+
+BOOL
+hub_unlock_tt(
+PUSB_DEV pdev,
+UCHAR port_idx,
+UCHAR type
+);
+
+
+VOID
+hub_post_clear_tt_event(
+PUSB_DEV pdev,
+BYTE port_idx,
+ULONG pipe
+);
+
+BOOL
+compdev_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+compdev_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+gendrv_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+gendrv_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+gendrv_if_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+gendrv_if_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+#endif
+
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/td.c b/reactos/drivers/usb/nt4compat/usbdriver/td.c
new file mode 100644
index 00000000000..a42fc8c6a45
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/td.c
@@ -0,0 +1,687 @@
+/**
+ * td.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "td.h"
+#include "debug.h"
+
+#define UHCI_MIN_TD_POOLS 4
+
+BOOL
+free_td_to_pool(
+PUHCI_TD_POOL ptd_pool,
+PUHCI_TD ptd
+); //add tds till pnext == NULL
+
+
+PUHCI_QH
+alloc_qh(
+PUHCI_QH_POOL pqh_pool
+); //null if failed
+
+BOOL
+init_td_pool(
+PUHCI_TD_POOL ptd_pool
+)
+{
+ int i, pages;
+ PTD_EXTENSION ptde;
+ PHYSICAL_ADDRESS phys_addr;
+
+ if (ptd_pool == NULL)
+ return FALSE;
+
+ if( ptd_pool->padapter == NULL)
+ return FALSE;
+
+ pages = sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE;
+ RtlZeroMemory( ptd_pool->td_array, sizeof( ptd_pool->td_array ) );
+ RtlZeroMemory( ptd_pool->logic_addr, sizeof( ptd_pool->logic_addr ) );
+
+ for( i = 0; i < pages; i++ )
+ {
+ ptd_pool->td_array[ i ] =
+ HalAllocateCommonBuffer(
+ ptd_pool->padapter,
+ PAGE_SIZE,
+ &ptd_pool->logic_addr[ i ],
+ FALSE);
+ if( ptd_pool->td_array[ i ] == NULL )
+ goto failed;
+ }
+
+ ptd_pool->tde_array = ( PTD_EXTENSION )usb_alloc_mem(
+ NonPagedPool,
+ sizeof( TD_EXTENSION ) * UHCI_MAX_POOL_TDS );
+
+ if (ptd_pool->tde_array == NULL)
+ goto failed;
+
+ for( i = 0; i < pages; i++ )
+ {
+ RtlZeroMemory( ptd_pool->td_array[ i ], PAGE_SIZE );
+ }
+
+ RtlZeroMemory(
+ ptd_pool->tde_array,
+ sizeof( TD_EXTENSION ) * UHCI_MAX_POOL_TDS );
+
+ ptde = ptd_pool->tde_array;
+ ptd_pool->free_count = 0;
+ ptd_pool->total_count = UHCI_MAX_POOL_TDS;
+ InitializeListHead( &ptd_pool->free_que);
+
+ for( i = 0; i < UHCI_MAX_POOL_TDS; i++ )
+ {
+ //link tde and the td one by one, fixed since this init
+ ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].ptde = &ptde[ i ];
+ ptde[ i ].ptd = &ptd_pool->td_array[ i >> 7 ][ i & 0x7f ];
+ ptde[ i ].flags = UHCI_ITEM_FLAG_TD;
+ ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].phy_addr = ptd_pool->logic_addr[ i >> 7 ].LowPart + ( i & 0x7f ) * sizeof( UHCI_TD );
+ ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].pool = ptd_pool;
+ ptd_pool->td_array[ i >> 7 ][ i & 0x7f ].purb = NULL;
+ free_td_to_pool( ptd_pool, &ptd_pool->td_array[ i >> 7 ][ i & 0x7f ]);
+
+ }
+ return TRUE;
+
+ failed:
+ for( i = 0; i < pages; i++ )
+ {
+ if( ptd_pool->td_array[ i ] )
+ {
+ HalFreeCommonBuffer( ptd_pool->padapter,
+ PAGE_SIZE,
+ ptd_pool->logic_addr[ i ],
+ ptd_pool->td_array[ i ],
+ FALSE);
+ ptd_pool->td_array[ i ] = NULL;
+ ptd_pool->logic_addr[ i ].QuadPart = 0;
+ }
+ }
+
+ if( ptd_pool->tde_array )
+ usb_free_mem( ptd_pool->tde_array);
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "init_td_pool(): failed to init the td pool\n" ) );
+ TRAP();
+
+ ptd_pool->free_count = ptd_pool->total_count = 0;
+ return FALSE;
+}
+
+BOOL
+free_td_to_pool(
+PUHCI_TD_POOL ptd_pool,
+PUHCI_TD ptd
+) //add tds till pnext == NULL
+{
+ if( ptd_pool == NULL || ptd == NULL )
+ {
+ return FALSE;
+ }
+
+ ptd->link = ptd->status = ptd->info = ptd->buffer = 0;
+ ptd->purb = NULL;
+ ptd_pool->free_count++;
+
+ InsertTailList( &ptd_pool->free_que, &ptd->ptde->vert_link);
+
+ return TRUE;
+
+}
+// qh routines
+PUHCI_TD
+alloc_td_from_pool(
+PUHCI_TD_POOL ptd_pool
+) //null if failed
+{
+ PTD_EXTENSION ptde;
+ PLIST_ENTRY temp;
+
+ if( ptd_pool == NULL )
+ return FALSE;
+
+ if( IsListEmpty( &ptd_pool->free_que ) )
+ return FALSE;
+
+ temp = RemoveHeadList( &ptd_pool->free_que );
+
+ if( temp == NULL )
+ return FALSE;
+
+ ptde = struct_ptr( temp, TD_EXTENSION, vert_link );
+
+ ptd_pool->free_count--;
+
+ InitializeListHead( &ptde->vert_link );
+ InitializeListHead( &ptde->hori_link );
+
+ return ptde->ptd;
+
+}
+
+BOOL
+is_pool_free(
+PUHCI_TD_POOL pool
+) //test whether the pool is all free
+{
+ if( pool == NULL )
+ return FALSE;
+
+ if( pool->free_count == pool->total_count )
+ return TRUE;
+
+ return FALSE;
+}
+
+BOOL
+is_pool_empty(
+PUHCI_TD_POOL pool
+)
+{
+ if( pool == NULL )
+ return FALSE;
+
+ return ( pool->free_count == 0 );
+}
+
+BOOL
+destroy_td_pool(
+PUHCI_TD_POOL ptd_pool
+)
+{
+ int i, pages;
+ PADAPTER_OBJECT padapter; //we need this garbage for allocation
+
+ padapter = ptd_pool->padapter;
+
+ pages = sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE;
+ if( ptd_pool && ptd_pool->padapter )
+ {
+ usb_free_mem( ptd_pool->tde_array );
+ ptd_pool->tde_array = NULL;
+ for( i = 0; i < pages; i++ )
+ {
+ if( ptd_pool->td_array[ i ] )
+ {
+ HalFreeCommonBuffer( ptd_pool->padapter,
+ PAGE_SIZE,
+ ptd_pool->logic_addr[ i ],
+ ptd_pool->td_array[ i ],
+ FALSE);
+ ptd_pool->td_array[ i ] = NULL;
+ ptd_pool->logic_addr[ i ].QuadPart = 0;
+ }
+ }
+ RtlZeroMemory( ptd_pool, sizeof( UHCI_TD_POOL ) );
+ ptd_pool->padapter = padapter;
+ ptd_pool->free_count = ptd_pool->total_count = 0;
+ }
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL
+init_td_pool_list(
+PUHCI_TD_POOL_LIST pool_list,
+PADAPTER_OBJECT padapter
+)
+{
+ int i;
+ RtlZeroMemory( pool_list, sizeof( UHCI_TD_POOL_LIST));
+ InitializeListHead( &pool_list->busy_pools );
+ InitializeListHead( &pool_list->free_pools );
+
+ pool_list->free_count = UHCI_MAX_TD_POOLS;
+ pool_list->free_tds = 0;
+
+ for( i = 0; i < UHCI_MAX_TD_POOLS; i++)
+ {
+ pool_list->pool_array[i].padapter = padapter;
+ InsertTailList( &pool_list->free_pools, &pool_list->pool_array[i].pool_link);
+ }
+
+ KeInitializeSpinLock( &pool_list->pool_lock );
+ return expand_pool_list( pool_list, UHCI_MIN_TD_POOLS );
+}
+
+BOOL
+destroy_td_pool_list(
+PUHCI_TD_POOL_LIST pool_list
+)
+{
+ PUHCI_TD_POOL pool;
+ while( IsListEmpty( &pool_list->busy_pools) == FALSE )
+ {
+ pool = (PUHCI_TD_POOL) RemoveHeadList( &pool_list->busy_pools);
+ destroy_td_pool( pool );
+ }
+
+ RtlZeroMemory( pool_list, sizeof( UHCI_TD_POOL_LIST ));
+ return TRUE;
+}
+
+BOOL
+expand_pool_list(
+PUHCI_TD_POOL_LIST pool_list,
+LONG pool_count
+) //private
+{
+ PUHCI_TD_POOL pool;
+ int i;
+
+ if( IsListEmpty( &pool_list->free_pools) == TRUE )
+ return FALSE;
+
+ if( pool_list->free_count < pool_count )
+ return FALSE;
+
+ for( i = 0; i < pool_count; i++ )
+ {
+ pool = (PUHCI_TD_POOL) RemoveHeadList( &pool_list->free_pools);
+
+ if( init_td_pool( pool ) == FALSE )
+ {
+ //reverse the allocation
+ InsertHeadList( &pool_list->free_pools, &pool->pool_link );
+ // collect_garbage( pool_list );
+ return FALSE;
+ }
+
+ InsertTailList( &pool_list->busy_pools, &pool->pool_link );
+ pool_list->free_tds += UHCI_MAX_POOL_TDS;
+ pool_list->free_count--;
+ }
+ return TRUE;
+}
+
+BOOL
+collect_garbage(
+PUHCI_TD_POOL_LIST pool_list
+)
+{
+ PLIST_ENTRY prev, next;
+
+ // no garbage
+ if( pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS )
+ return TRUE;
+
+ ListFirstPrev( &pool_list->busy_pools, prev);
+ ListNext( &pool_list->busy_pools, prev, next);
+
+ while( next && next != &pool_list->busy_pools)
+ {
+ if( is_pool_free( (PUHCI_TD_POOL)next ) )
+ {
+ RemoveEntryList( next );
+ destroy_td_pool( ( PUHCI_TD_POOL )next );
+ InsertTailList( &pool_list->free_pools, next);
+ pool_list->free_count++;
+ pool_list->free_tds -= UHCI_MAX_POOL_TDS;
+ ListNext( &pool_list->busy_pools, prev, next);
+ if( pool_list->free_count >= UHCI_MAX_TD_POOLS - UHCI_MIN_TD_POOLS )
+ break;
+ }
+ else
+ {
+ prev = next;
+ ListNext( &pool_list->busy_pools, prev, next);
+ }
+ }
+ return TRUE;
+
+}
+
+//private
+LONG
+get_num_free_tds(
+PUHCI_TD_POOL_LIST pool_list
+)
+{
+ return pool_list->free_tds;
+}
+
+//private
+LONG
+get_max_free_tds(
+PUHCI_TD_POOL_LIST pool_list
+)
+{
+ return pool_list->free_tds + pool_list->free_count * UHCI_MAX_POOL_TDS;
+}
+
+BOOL
+free_td(
+PUHCI_TD_POOL_LIST pool_list,
+PUHCI_TD ptd
+) //add tds till pnext == NULL
+{
+ if( pool_list == NULL || ptd == NULL )
+ return FALSE;
+
+ if( free_td_to_pool( ptd->pool, ptd ) == FALSE )
+ return FALSE;
+
+ pool_list->free_tds++;
+
+ if( is_pool_free( ptd->pool ) )
+ {
+ collect_garbage( pool_list );
+ }
+ return TRUE;
+}
+
+PUHCI_TD
+alloc_td(
+PUHCI_TD_POOL_LIST pool_list
+) //null if failed
+{
+ PLIST_ENTRY prev, next;
+ PUHCI_TD new_td;
+
+ if( pool_list == NULL )
+ return NULL;
+
+ if( pool_list->free_tds == 0 )
+ {
+ if( expand_pool_list( pool_list, 1 ) == FALSE )
+ return NULL;
+ }
+
+ ListFirst( &pool_list->busy_pools, prev );
+
+ while( prev && prev != &pool_list->busy_pools )
+ {
+ if( is_pool_empty( (PUHCI_TD_POOL) prev ) == FALSE )
+ {
+ new_td = alloc_td_from_pool( (PUHCI_TD_POOL) prev );
+
+ if( new_td == NULL )
+ TRAP();
+
+ pool_list->free_tds--;
+
+ return new_td;
+ }
+
+ ListNext( &pool_list->busy_pools, prev, next);
+ prev = next;
+ }
+
+ return NULL;
+}
+
+PUHCI_TD
+alloc_tds(
+PUHCI_TD_POOL_LIST pool_list,
+LONG count
+)
+{
+ //return value is a list of tds, vert_link chain.
+
+ LONG i;
+ PUHCI_TD ptd, pnext;
+
+ if( pool_list == NULL || count <= 0 )
+ return NULL;
+
+ if( count >= get_max_free_tds( pool_list ) )
+ return NULL;
+
+ ptd = alloc_td( pool_list );
+
+ for( i = 1; i < count; i ++ )
+ {
+ pnext = alloc_td( pool_list );
+
+ if( pnext )
+ {
+ InsertTailList( &ptd->ptde->vert_link, &pnext->ptde->vert_link );
+ }
+ else
+ TRAP();
+ }
+
+ uhci_dbg_print( DBGLVL_MEDIUM, ("alloc_tds(): td pool-list free_tds=0x%x, free pools=0x%x\n", \
+ pool_list->free_tds,
+ pool_list->free_count ) );
+
+ return ptd;
+
+}
+
+VOID
+free_tds(
+PUHCI_TD_POOL_LIST pool_list,
+PUHCI_TD ptd
+)
+{
+ PUHCI_TD ptofree;
+ PLIST_ENTRY pthis;
+
+ if( pool_list == NULL || ptd == NULL )
+ return;
+
+ while( IsListEmpty( &ptd->ptde->vert_link ) == FALSE )
+ {
+ pthis = RemoveHeadList( &ptd->ptde->vert_link )
+ ptofree = ( ( PTD_EXTENSION)pthis )->ptd;
+ free_td( pool_list, ptofree );
+ }
+
+ free_td( pool_list, ptd );
+ return;
+}
+
+
+
+BOOL
+can_transfer(
+PUHCI_TD_POOL_LIST pool_list,
+LONG td_count
+)
+{
+ if( td_count > get_max_free_tds( pool_list ) )
+ return FALSE;
+
+ return TRUE;
+}
+
+VOID
+lock_td_pool(
+PUHCI_TD_POOL_LIST pool_list,
+BOOL at_dpc
+)
+{
+ //if( !at_dpc )
+ // KeAcquireSpinLock( &pool_list->pool_lock );
+ //else
+ // KeAcquireSpinLockAtDpcLevel( &pool_list->pool_lock );
+}
+
+VOID
+unlock_td_pool(
+PUHCI_TD_POOL_LIST pool_list,
+BOOL at_dpc
+)
+{
+ //if( !at_dpc )
+ // KeReleaseSpinLock( &pool_list->pool_lock );
+ //else
+ // KeReleaseSpinLockFromDpcLevel( &pool_list->pool_lock );
+}
+
+BOOL
+init_qh_pool(
+PUHCI_QH_POOL pqh_pool,
+PADAPTER_OBJECT padapter
+)
+{
+ PQH_EXTENSION pqhe;
+ LONG i;
+
+ if( pqh_pool == NULL || padapter == NULL )
+ return FALSE;
+
+ pqh_pool->padapter = padapter;
+
+ pqh_pool->qhe_array = (PQH_EXTENSION)usb_alloc_mem(
+ NonPagedPool,
+ sizeof(QH_EXTENSION) * UHCI_MAX_POOL_QHS);
+
+ if (pqh_pool->qhe_array == NULL)
+ return FALSE;
+
+ pqh_pool->qh_array = \
+ (PUHCI_QH)HalAllocateCommonBuffer(
+ padapter,
+ sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS,
+ &pqh_pool->logic_addr,
+ FALSE);
+
+ if( pqh_pool->qh_array == NULL )
+ {
+ usb_free_mem( pqh_pool->qhe_array );
+ pqh_pool->qhe_array = NULL;
+ return FALSE;
+ }
+
+ pqhe = pqh_pool->qhe_array;
+
+ pqh_pool->free_count = 0;
+ pqh_pool->total_count = UHCI_MAX_POOL_TDS;
+
+ KeInitializeSpinLock( &pqh_pool->pool_lock);
+ InitializeListHead( &pqh_pool->free_que);
+
+
+ for(i = 0; i < UHCI_MAX_POOL_QHS; i++)
+ {
+ pqh_pool->qh_array[i].pqhe = &pqhe[i];
+ pqhe[i].pqh = &pqh_pool->qh_array[i];
+
+ pqh_pool->qh_array[i].phy_addr = ( pqh_pool->logic_addr.LowPart + ( sizeof( UHCI_QH) * i ) ) | UHCI_PTR_QH;
+ //pqh_pool->qh_array[i].reserved = 0;
+
+ //always breadth first
+ pqhe[i].flags = UHCI_ITEM_FLAG_QH;
+
+ free_qh( pqh_pool, &pqh_pool->qh_array[i] );
+
+ }
+ return TRUE;
+
+}
+
+BOOL
+free_qh(
+PUHCI_QH_POOL pqh_pool,
+PUHCI_QH pqh
+) //add qhs till pnext == NULL
+{
+ if( pqh_pool == NULL || pqh == NULL)
+ return FALSE;
+
+ pqh->link = pqh->element = 0;
+ pqh->pqhe->purb = NULL;
+ InsertTailList( &pqh_pool->free_que, &pqh->pqhe->vert_link);
+ pqh_pool->free_count ++;
+
+ return TRUE;
+}
+
+PUHCI_QH
+alloc_qh(
+PUHCI_QH_POOL pqh_pool
+) //null if failed
+{
+ PQH_EXTENSION pqhe;
+
+ if( pqh_pool == NULL )
+ return FALSE;
+
+ if( IsListEmpty( &pqh_pool->free_que))
+ return FALSE;
+
+ pqhe = ( PQH_EXTENSION )RemoveHeadList( &pqh_pool->free_que );
+
+ if( pqhe )
+ {
+ InitializeListHead( &pqhe->hori_link );
+ InitializeListHead( &pqhe->vert_link );
+ return pqhe->pqh;
+ }
+ return NULL;
+
+}
+
+BOOL
+destroy_qh_pool(
+PUHCI_QH_POOL pqh_pool
+)
+{
+ int i;
+
+ if(pqh_pool)
+ {
+ usb_free_mem(pqh_pool->qhe_array);
+
+ HalFreeCommonBuffer( pqh_pool->padapter,
+ sizeof(UHCI_QH) * UHCI_MAX_POOL_QHS,
+ pqh_pool->logic_addr,
+ pqh_pool->qh_array,
+ FALSE);
+
+ RtlZeroMemory( pqh_pool, sizeof( UHCI_QH_POOL ) );
+
+ }
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+VOID
+lock_qh_pool(
+PUHCI_QH_POOL pool,
+BOOL at_dpc
+)
+{
+ //if( !at_dpc )
+ // KeAcquireSpinLock( &pool->pool_lock );
+ //else
+ // KeAcquireSpinLockAtDpcLevel( &pool->pool_lock );
+}
+
+VOID
+unlock_qh_pool(
+PUHCI_QH_POOL pool,
+BOOL at_dpc
+)
+{
+ //if( !at_dpc )
+ // KeReleaseSpinLock( &pool->pool_lock );
+ //else
+ // KeReleaseSpinLockFromDpcLevel( &pool->pool_lock );
+}
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/td.h b/reactos/drivers/usb/nt4compat/usbdriver/td.h
new file mode 100644
index 00000000000..72c74a6c67b
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/td.h
@@ -0,0 +1,1031 @@
+/*
+ * NOTE!!!: part of the code is pasted from linux/driver/usb/uhci.h
+ */
+
+#ifndef __UHCI_H__
+#define __UHCI_H__
+
+#include "usb.h"
+#include "ntddk.h"
+#include "hcd.h"
+
+#define BOOL ULONG
+#define LIST_HEAD LIST_ENTRY
+#define PLIST_HEAD PLIST_ENTRY
+#define BYTE UCHAR
+#define PBYTE PUCHAR
+#define WORD USHORT
+#define DWORD ULONG
+
+#define LOWORD(l) ( (WORD) ( ( l ) & 0xffff ) )
+#define HIWORD(l) ( (WORD) ( ( l ) >> 16 ) )
+
+#define PCI_MAX_FUNCTIONS 8
+
+
+#define UHCI_MAX_POOL_TDS 1024 //8 pages of 4k
+#define UHCI_MAX_POOL_QHS 256 //1 page of 4k
+#define UHCI_MAX_ANT_TDS 0x20e82
+
+#define UHCI_MAX_TD_POOLS 8
+#define UHCI_MAX_TDS_PER_TRANSFER UHCI_MAX_POOL_TDS
+
+#define UHCI_FULL_SPEED_BANDWIDHT ( 12 * 1000 * 1000 )
+#define UHCI_LOW_SPEED_BANDWIDHT ( 15 * 100 * 1000 )
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD 0
+#define USBCMD_RS 0x0001 /* Run/Stop */
+#define USBCMD_HCRESET 0x0002 /* Host reset */
+#define USBCMD_GRESET 0x0004 /* Global reset */
+#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define USBCMD_FGR 0x0010 /* Force Global Resume */
+#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
+#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
+#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS 2
+#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
+#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
+#define USBSTS_RD 0x0004 /* Resume Detect */
+#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
+#define USBSTS_HCH 0x0020 /* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR 4
+#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
+#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
+#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
+
+#define USBFRNUM 6
+#define USBFLBASEADD 8
+#define USBSOF 12
+
+/* USB port status and control registers */
+#define USBPORTSC1 16
+#define USBPORTSC2 18
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
+#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
+#define USBPORTSC_PE 0x0004 /* Port Enable */
+#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
+#define USBPORTSC_LS 0x0020 /* Line Status */
+#define USBPORTSC_RD 0x0040 /* Resume Detect */
+#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
+#define USBPORTSC_PR 0x0200 /* Port Reset */
+#define USBPORTSC_SUSP 0x1000 /* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */
+
+#define UHCI_PTR_BITS 0x000F
+#define UHCI_PTR_TERM 0x0001
+#define UHCI_PTR_QH 0x0002
+#define UHCI_PTR_DEPTH 0x0004
+
+#define UHCI_MAX_FRAMES 1024 /* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
+
+
+//from linux's uhci.h
+#define UHCI_MAX_SKELTDS 10
+#define skel_int1_td skel_td[0]
+#define skel_int2_td skel_td[1]
+#define skel_int4_td skel_td[2]
+#define skel_int8_td skel_td[3]
+#define skel_int16_td skel_td[4]
+#define skel_int32_td skel_td[5]
+#define skel_int64_td skel_td[6]
+#define skel_int128_td skel_td[7]
+#define skel_int256_td skel_td[8]
+#define skel_term_td skel_td[9] /* To work around PIIX UHCI bug */
+
+#define UHCI_MAX_SKELQHS 4
+#define skel_ls_control_qh skel_qh[0]
+#define skel_hs_control_qh skel_qh[1]
+#define skel_bulk_qh skel_qh[2]
+#define skel_term_qh skel_qh[3]
+
+
+
+struct URB;
+
+/*
+ * for TD :
+ */
+#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT 27
+#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
+#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
+#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
+#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
+#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
+#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
+#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
+#define TD_CTRL_NAK (1 << 19) /* NAK Received */
+#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+ TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+/*
+ * for TD : (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE 19
+#define TD_PID 0xFF
+
+#define uhci_maxlen(token) ((token) >> 21)
+#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token) (((token) >> 15) & 0xf)
+#define uhci_devaddr(token) (((token) >> 8) & 0x7f)
+#define uhci_devep(token) (((token) >> 8) & 0x7ff)
+#define uhci_packetid(token) ((token) & 0xff)
+#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
+
+/*
+ * The documentation says "4 dwords for hardware, 4 dwords for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 dwords for software, woops.
+ * Everything still works tho, surprise! -jerdfelt
+ */
+
+#define UHCI_ITEM_FLAG_TYPE 0x3
+#define UHCI_ITEM_FLAG_TD 0x0
+#define UHCI_ITEM_FLAG_QH 0x1
+
+#define offsetof( s, m ) ( ( ULONG )( &( ( s* )0 )->m ) )
+
+#define struct_ptr( meMBER_ptr, stRUCT_name, meMBER_name ) \
+( ( stRUCT_name* ) ( ( (CHAR*)( meMBER_ptr ) ) - offsetof( stRUCT_name, meMBER_name ) ) )
+
+#define dev_state( pdEV ) ( ( pdEV )->flags & USB_DEV_STATE_MASK )
+
+#define dev_class( pdev ) ( pdev->flags & USB_DEV_CLASS_MASK )
+
+#define uhci_from_hcd( hCD ) ( struct_ptr( ( hCD ), UHCI_DEV, hcd_interf ) )
+#define uhci_from_dev( dEV ) ( struct_ptr( ( dEV->hcd ), UHCI_DEV, hcd_interf ) )
+
+typedef struct _TD_EXTENSION
+{
+ LIST_ENTRY vert_link; // urb will use this to link qh and tds
+ LIST_ENTRY hori_link;
+ ULONG flags;
+ struct _UHCI_TD *ptd; //link to is companion td, constant since initialized
+
+} TD_EXTENSION, *PTD_EXTENSION;
+
+typedef struct _UHCI_TD
+{
+ /* Hardware fields */
+ ULONG link;
+ ULONG status;
+ ULONG info;
+ ULONG buffer;
+
+ /* Software fields */
+ ULONG phy_addr;
+ PTD_EXTENSION ptde;
+ struct _URB *purb;
+ struct _UHCI_TD_POOL *pool; //pool this td belongs to
+
+} UHCI_TD, *PUHCI_TD;
+
+typedef struct _UHCI_TD_POOL
+{
+
+ LIST_ENTRY pool_link; //link to next and prev td pool
+ PUHCI_TD td_array[ sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE ]; //would be allcated in common buffer
+ PHYSICAL_ADDRESS logic_addr[ sizeof( UHCI_TD ) * UHCI_MAX_POOL_TDS / PAGE_SIZE ]; //logical addr of the array
+
+ PTD_EXTENSION tde_array;
+ LIST_ENTRY free_que; //list of free tds
+ LONG free_count; //free tds in this pool
+ LONG total_count; //total count of the tds
+ PADAPTER_OBJECT padapter;
+
+} UHCI_TD_POOL, *PUHCI_TD_POOL;
+
+BOOL
+init_td_pool(
+PUHCI_TD_POOL pool
+);
+
+BOOL
+free_td_to_pool(
+PUHCI_TD_POOL pool,
+PUHCI_TD ptd
+); //add tds till pnext == NULL
+
+PUHCI_TD
+alloc_td_from_pool(
+PUHCI_TD_POOL ptd_pool
+); //null if failed]
+
+BOOL
+is_pool_free(
+PUHCI_TD_POOL pool
+); //test whether the pool is all free
+
+BOOL
+is_pool_full(
+PUHCI_TD_POOL pool
+);
+
+BOOL
+destroy_td_pool(
+PUHCI_TD_POOL pool
+);
+
+typedef struct _UHCI_TD_POOL_LIST
+{
+ LIST_ENTRY busy_pools;
+ KSPIN_LOCK pool_lock;
+ LONG free_tds; //free tds in all the pool
+ LONG free_count; //free pool count
+ LIST_ENTRY free_pools;
+ UHCI_TD_POOL pool_array[UHCI_MAX_TD_POOLS]; //max transfer is 640k
+
+} UHCI_TD_POOL_LIST, *PUHCI_TD_POOL_LIST;
+
+BOOL
+init_td_pool_list(
+PUHCI_TD_POOL_LIST pool_list,
+PADAPTER_OBJECT padapter
+);
+
+BOOL
+destroy_td_pool_list(
+PUHCI_TD_POOL_LIST pool_list
+);
+
+BOOL
+expand_pool_list(
+PUHCI_TD_POOL_LIST pool_list,
+LONG pool_count
+); //private
+
+BOOL
+collect_garbage(
+PUHCI_TD_POOL_LIST pool_list
+);
+
+static LONG
+get_free_tds(
+PUHCI_TD_POOL_LIST pool_list
+); //private
+
+static LONG
+get_max_free_tds(
+PUHCI_TD_POOL_LIST pool_list
+); //private
+
+BOOL
+can_transfer(
+PUHCI_TD_POOL_LIST pool_list,
+LONG td_count);
+
+BOOL
+free_td(
+PUHCI_TD_POOL_LIST pool_list,
+PUHCI_TD ptd
+); //add tds till pnext == NULL
+
+PUHCI_TD
+alloc_td(
+PUHCI_TD_POOL_LIST pool_list
+); //null if failed
+
+VOID
+lock_td_pool(
+PUHCI_TD_POOL_LIST pool_list,
+BOOL at_dpc
+);
+
+VOID
+unlock_td_pool(
+PUHCI_TD_POOL_LIST pool_list,
+BOOL at_dpc
+);
+
+typedef struct _UHCI_QH
+{
+ /* Hardware fields */
+ ULONG link; // Next queue
+ ULONG element; // Queue element pointer
+
+ /* Software fields */
+ ULONG phy_addr; //constant since initialized
+ struct _QH_EXTENSION *pqhe;
+
+} UHCI_QH, *PUHCI_QH;
+
+typedef struct _QH_EXTENSION
+{
+
+ LIST_ENTRY vert_link;
+ LIST_ENTRY hori_link;
+ ULONG flags;
+ PUHCI_QH pqh; //constant since initialized
+ struct _URB *purb;
+
+} QH_EXTENSION, *PQH_EXTENSION;
+
+typedef struct _UHCI_QH_POOL
+{
+
+ PUHCI_QH qh_array;
+ PHYSICAL_ADDRESS logic_addr; //logical addr of the array
+
+ PQH_EXTENSION qhe_array;
+ LIST_ENTRY free_que;
+ LONG free_count;
+ LONG total_count;
+ KSPIN_LOCK pool_lock;
+ PADAPTER_OBJECT padapter; //we need this garbage for allocation
+
+} UHCI_QH_POOL, *PUHCI_QH_POOL;
+
+
+BOOL
+init_qh_pool(
+PUHCI_QH_POOL pool,
+PADAPTER_OBJECT padapter
+);
+
+BOOL
+free_qh(
+PUHCI_QH_POOL pool,
+PUHCI_QH ptd
+); //add qhs till pnext == NULL
+
+PUHCI_QH
+alloc_qh(
+PUHCI_QH_POOL pool
+); //null if failed
+
+BOOL
+destroy_qh_pool(
+PUHCI_QH_POOL pool
+);
+
+VOID
+lock_qh_pool(
+PUHCI_QH_POOL pool,
+BOOL at_dpc
+);
+
+VOID
+unlock_qh_pool(
+PUHCI_QH_POOL pool,
+BOOL at_dpc
+);
+
+/*
+ * Search tree for determining where fits in the
+ * skelqh[] skeleton.
+ *
+ * An interrupt request should be placed into the slowest skelqh[]
+ * which meets the interval/period/frequency requirement.
+ * An interrupt request is allowed to be faster than but not slower.
+ *
+ * For a given , this function returns the appropriate/matching
+ * skelqh[] index value.
+ *
+ * NOTE: For UHCI, we don't really need int256_qh since the maximum interval
+ * is 255 ms. However, we do need an int1_qh since 1 is a valid interval
+ * and we should meet that frequency when requested to do so.
+ * This will require some change(s) to the UHCI skeleton.
+ */
+static int __interval_to_skel(int interval)
+{
+ if (interval < 16) {
+ if (interval < 4) {
+ if (interval < 2)
+ return 0; /* int1 for 0-1 ms */
+ return 1; /* int2 for 2-3 ms */
+ }
+ if (interval < 8)
+ return 2; /* int4 for 4-7 ms */
+ return 3; /* int8 for 8-15 ms */
+ }
+ if (interval < 64) {
+ if (interval < 32)
+ return 4; /* int16 for 16-31 ms */
+ return 5; /* int32 for 32-63 ms */
+ }
+ if (interval < 128)
+ return 6; /* int64 for 64-127 ms */
+ return 7; /* int128 for 128-255 ms (Max.) */
+}
+
+#define USB_ENDP_FLAG_BUSY_MASK 0x0000ff00
+#define USB_ENDP_FLAG_STAT_MASK 0xff
+#define USB_ENDP_FLAG_STALL 0x01
+#define USB_ENDP_FLAG_DATATOGGLE 0x80000000
+#define USB_ENDP_FLAG_DEFAULT_ENDP 0x40000000
+
+#define usb_endp_busy_count( peNDP ) \
+( ( ( peNDP )->flags & USB_ENDP_FLAG_BUSY_MASK ) >> 8 )
+
+#define usb_endp_busy_count_inc( peNDP ) \
+( peNDP->flags = ( ( ( ( usb_endp_busy_count( peNDP ) + 1 ) << 8 ) & USB_ENDP_FLAG_BUSY_MASK ) | \
+( ( peNDP )->flags & ~USB_ENDP_FLAG_BUSY_MASK ) ) )
+
+#define usb_endp_busy_count_dec( peNDP ) \
+( peNDP->flags = ( ( ( ( usb_endp_busy_count( peNDP ) - 1 ) << 8 ) & USB_ENDP_FLAG_BUSY_MASK )| \
+( ( peNDP )->flags & ~USB_ENDP_FLAG_BUSY_MASK ) ) )
+
+typedef struct _USB_ENDPOINT
+{
+ ULONG flags; //toggle | busy-count | stall | default-endp; busy count( usually 1 when busy, may be greater if iso endp )
+ LIST_ENTRY urb_list; //pending urb queue
+
+ struct _USB_INTERFACE *pusb_if;
+ struct _USB_ENDPOINT_DESC *pusb_endp_desc;
+
+} USB_ENDPOINT, *PUSB_ENDPOINT;
+
+#define MAX_ENDPS_PER_IF 10
+
+typedef struct _USB_INTERFACE
+{
+ UCHAR endp_count;
+ USB_ENDPOINT endp[MAX_ENDPS_PER_IF];
+
+
+ struct _USB_CONFIGURATION *pusb_config;
+ PUSB_INTERFACE_DESC pusb_if_desc;
+
+ struct _USB_DRIVER *pif_drv; //for hub_dev use
+ PVOID if_ext;
+ LONG if_ext_size;
+ UCHAR altif_count;
+ LIST_ENTRY altif_list;
+
+
+} USB_INTERFACE, *PUSB_INTERFACE;
+
+#define MAX_INTERFACES_PER_CONFIG 4
+#define MAX_CONFIGS_PER_DEV 4
+
+typedef struct _USB_CONFIGURATION
+{
+ //only for active configuration
+ struct _USB_CONFIGURATION_DESC *pusb_config_desc;
+ UCHAR if_count;
+ USB_INTERFACE interf[MAX_INTERFACES_PER_CONFIG];
+ struct _USB_DEV *pusb_dev;
+
+} USB_CONFIGURATION, *PUSB_CONFIGURATION;
+
+#define USE_IRQL \
+KIRQL _pending_endp_lock_old_irql, _pending_endp_list_lock_old_irql, _dev_lock_old_irql, old_irql;
+
+#define USB_DEV_STATE_MASK ( 0xff << 8 )
+#define USB_DEV_STATE_POWERED ( 0x01 << 8 )
+#define USB_DEV_STATE_RESET ( 0x02 << 8 )
+#define USB_DEV_STATE_ADDRESSED ( 0x03 << 8 )
+#define USB_DEV_STATE_FIRST_CONFIG ( 0x04 << 8 )
+#define USB_DEV_STATE_RECONFIG ( 0x05 << 8 )
+#define USB_DEV_STATE_CONFIGURED ( 0x06 << 8 )
+#define USB_DEV_STATE_SUSPENDED ( 0x07 << 8 )
+#define USB_DEV_STATE_BEFORE_ZOMB ( 0x08 << 8 )
+#define USB_DEV_STATE_ZOMB ( 0x09 << 8 )
+
+#define USB_DEV_CLASS_MASK ( 0xff << 16 )
+#define USB_DEV_CLASS_HUB ( USB_CLASS_HUB << 16 )
+#define USB_DEV_CLASS_MASSSTOR ( USB_CLASS_MASS_STORAGE << 16 )
+#define USB_DEV_CLASS_ROOT_HUB ( ( USB_CLASS_VENDOR_SPEC - 2 ) << 16 )
+#define USB_DEV_CLASS_SCANNER ( ( USB_DEV_CLASS_ROOT_HUB - 1 ) << 16 )
+
+#define USB_DEV_FLAG_HIGH_SPEED 0x20 // high speed dev for usb2.0
+#define USB_DEV_FLAG_LOW_SPEED 0x40 // note: this bit is shared in urb->pipe
+#define USB_DEV_FLAG_IF_DEV 0x01 // this dev is a virtual dev, that is a interface
+
+#define lock_dev( pdev, at_dpc ) \
+{\
+ KIRQL cur_irql;\
+ cur_irql = KeGetCurrentIrql();\
+ if( cur_irql == DISPATCH_LEVEL )\
+ {\
+ KeAcquireSpinLockAtDpcLevel( &pdev->dev_lock );\
+ _dev_lock_old_irql = DISPATCH_LEVEL;\
+ }\
+ else if( cur_irql < DISPATCH_LEVEL )\
+ KeAcquireSpinLock( &pdev->dev_lock, &_dev_lock_old_irql );\
+ else\
+ TRAP();\
+}
+
+#define unlock_dev( pdev, from_dpc ) \
+{\
+ if( _dev_lock_old_irql == DISPATCH_LEVEL )\
+ KeReleaseSpinLockFromDpcLevel( &pdev->dev_lock );\
+ else if( _dev_lock_old_irql < DISPATCH_LEVEL )\
+ KeReleaseSpinLock( &pdev->dev_lock, _dev_lock_old_irql );\
+ else\
+ TRAP();\
+}
+
+typedef struct _USB_DEV
+{
+ LIST_ENTRY dev_link; //for dev-list
+
+ KSPIN_LOCK dev_lock;
+ PDEVICE_OBJECT dev_obj;
+ ULONG flags; //class | cur_state | low speed
+ LONG ref_count; //client count
+
+ UCHAR dev_addr; //usb addr
+ ULONG dev_id; //will be used to compose dev handle
+
+ struct _USB_DEV *parent_dev;
+ UCHAR port_idx; //parent hub's port idx, to which the dev attached
+
+ struct _HCD *hcd; //point to the hcd the dev belongs to
+
+ USB_ENDPOINT default_endp; //control endp. its interfac pointer is to the first interface
+
+ LONG desc_buf_size;
+ PUCHAR desc_buf;
+ struct _USB_DEVICE_DESC *pusb_dev_desc;
+
+ UCHAR active_config_idx;
+ PUSB_CONFIGURATION usb_config; //the active configuration
+
+ struct _USB_DRIVER *dev_driver;
+ PVOID dev_ext;
+ LONG dev_ext_size;
+
+ LONG time_out_count; //default pipe error counter, three time-outs will cause the dev not function
+ LONG error_count; //usb transfer error counter for statics only
+
+} USB_DEV, *PUSB_DEV;
+
+// pending endpoint pool definitions
+
+#define UHCI_MAX_PENDING_ENDPS 32
+
+typedef struct _UHCI_PENDING_ENDP
+{
+ LIST_ENTRY endp_link;
+ PUSB_ENDPOINT pendp;
+
+} UHCI_PENDING_ENDP, *PUHCI_PENDING_ENDP;
+
+typedef struct _UHCI_PENDING_ENDP_POOL
+{
+ PUHCI_PENDING_ENDP pending_endp_array;
+ LIST_ENTRY free_que;
+ LONG free_count;
+ LONG total_count;
+ KSPIN_LOCK pool_lock;
+
+} UHCI_PENDING_ENDP_POOL, *PUHCI_PENDING_ENDP_POOL;
+
+BOOL
+init_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+);
+
+BOOL
+free_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+PUHCI_PENDING_ENDP pending_endp
+);
+
+PUHCI_PENDING_ENDP
+alloc_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+LONG count
+);
+
+BOOL
+destroy_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+);
+
+// pool type is PUHCI_PENDING_ENDP_POOL
+#define lock_pending_endp_pool( pool ) \
+{\
+ KeAcquireSpinLock( &pool->pool_lock, &_pending_endp_lock_old_irql );\
+}
+
+#define unlock_pending_endp_pool( pool ) \
+{\
+ KeReleaseSpinLock( &pool->pool_lock, &_pending_endp_lock_old_irql );\
+}
+
+
+// end of pending endpoint pool
+typedef struct _FRAME_LIST_CPU_ENTRY
+{
+ LIST_ENTRY td_link;
+
+} FRAME_LIST_CPU_ENTRY, *PFRAME_LIST_CPU_ENTRY;
+
+#define uhci_public_res_lock pending_endp_list_lock
+#define uhci_status( _uhci_ ) ( READ_PORT_USHORT( ( PUSHORT )( ( _uhci_ )->port_base + USBSTS ) ) )
+
+typedef struct _UHCI
+{
+ PHYSICAL_ADDRESS uhci_reg_base; // io space
+ BOOL port_mapped;
+ PBYTE port_base;
+
+ PHYSICAL_ADDRESS io_buf_logic_addr;
+ PBYTE io_buf;
+
+ PHYSICAL_ADDRESS frame_list_logic_addr;
+
+ KSPIN_LOCK frame_list_lock; //run at DIRQL
+ PULONG frame_list;
+ PFRAME_LIST_CPU_ENTRY frame_list_cpu;
+ LIST_HEAD urb_list; //active urb-list
+ PUHCI_TD skel_td[UHCI_MAX_SKELTDS];
+ PUHCI_QH skel_qh[UHCI_MAX_SKELQHS]; //skeltons
+
+
+
+ UHCI_TD_POOL_LIST td_pool;
+ UHCI_QH_POOL qh_pool;
+
+
+ //for iso and int bandwidth claim, bandwidth schedule
+ KSPIN_LOCK pending_endp_list_lock; //lock to access the following two
+ LIST_HEAD pending_endp_list;
+ UHCI_PENDING_ENDP_POOL pending_endp_pool;
+ PLONG frame_bw;
+ LONG fsbr_cnt; //used to record number of fsbr users
+
+ KTIMER reset_timer; //used to reset the host controller
+
+ //struct _USB_DEV_MANAGER dev_mgr; //it is in hcd_interf
+ struct _DEVICE_EXTENSION *pdev_ext;
+
+ PUSB_DEV root_hub; //root hub
+ HCD hcd_interf;
+
+} UHCI_DEV, *PUHCI_DEV;
+
+#define lock_pending_endp_list( list_lock ) \
+{\
+ KeAcquireSpinLock( list_lock, &_pending_endp_list_lock_old_irql );\
+}
+
+#define unlock_pending_endp_list( list_lock ) \
+{\
+ KeReleaseSpinLock( list_lock, _pending_endp_list_lock_old_irql );\
+}
+typedef struct _UHCI_INTERRUPT
+{
+ ULONG level;
+ ULONG vector;
+ ULONG affinity;
+
+} UHCI_INTERRUPT, *PUHCI_INTERRUPT;
+
+typedef struct _UHCI_PORT
+{
+ PHYSICAL_ADDRESS Start;
+ ULONG Length;
+
+} UHCI_PORT, *PUHCI_PORT;
+
+typedef NTSTATUS ( *PDISPATCH_ROUTINE )( PDEVICE_OBJECT dev_obj, PIRP irp );
+
+#define NTDEV_TYPE_HCD 1
+#define NTDEV_TYPE_CLIENT_DEV 2
+
+typedef struct _DEVEXT_HEADER
+{
+ ULONG type;
+ PDISPATCH_ROUTINE dispatch;
+ PDRIVER_STARTIO start_io;
+
+ struct _USB_DEV_MANAGER *dev_mgr; //mainly for use by cancel irp
+
+} DEVEXT_HEADER, *PDEVEXT_HEADER;
+
+typedef struct _DEVICE_EXTENSION
+{
+ //struct _USB_DEV_MANAGER *pdev_mgr;
+ DEVEXT_HEADER dev_ext_hdr;
+ PDEVICE_OBJECT pdev_obj;
+ PDRIVER_OBJECT pdrvr_obj;
+ PUHCI_DEV uhci;
+
+ //device resources
+ PADAPTER_OBJECT padapter;
+ ULONG map_regs;
+ PCM_RESOURCE_LIST res_list;
+ ULONG pci_addr; // bus number | slot number | funciton number
+ UHCI_INTERRUPT res_interrupt;
+ UHCI_PORT res_port;
+
+ PKINTERRUPT uhci_int;
+ KDPC uhci_dpc;
+
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+
+//helper macro
+#define ListFirst( heAD, firST) \
+{\
+ if( IsListEmpty( ( heAD ) ) )\
+ firST = NULL;\
+ else\
+ firST = ( heAD )->Flink;\
+}
+
+#define ListNext( heAD, curreNT, neXT) \
+{\
+ if( IsListEmpty( ( heAD ) ) == FALSE )\
+ {\
+ neXT = (curreNT)->Flink;\
+ if( neXT == heAD )\
+ neXT = NULL;\
+ }\
+ else\
+ neXT = NULL;\
+}
+
+#define ListPrev( heAD, curreNT, prEV) \
+{\
+ if( IsListEmpty( ( heAD ) ) == FALSE )\
+ {\
+ prEV = (curreNT)->Blink;\
+ if( prEV == heAD )\
+ prEV = NULL;\
+ else\
+ prEV = NULL;\
+}
+
+#define ListFirstPrev( heAD, firST) \
+{\
+ if( IsListEmpty( ( heAD ) ) )\
+ firST = NULL;\
+ else\
+ firST = ( heAD )->Blink;\
+}
+
+#define MergeList( liST1, liST2 )\
+{\
+ PLIST_ENTRY taIL1, taIL2;\
+ if( IsListEmpty( liST2 ) == TRUE )\
+ {\
+ InsertTailList( liST1, liST2 );\
+ }\
+ else if( IsListEmpty( liST1 ) == TRUE )\
+ {\
+ InsertTailList( liST2, liST1 );\
+ }\
+ else\
+ {\
+ ListFirstPrev( liST1, taIL1 );\
+ ListFirstPrev( liST2, taIL2 );\
+\
+ taIL1->Flink = ( liST2 );\
+ ( liST2 )->Blink = taIL1;\
+\
+ taIL2->Flink = ( liST1 );\
+ ( liST1 )->Blink = taIL2;\
+ }\
+}
+
+PUHCI_TD
+alloc_tds(
+PUHCI_TD_POOL_LIST pool_list,
+LONG count
+);
+
+VOID
+free_tds(
+PUHCI_TD_POOL_LIST pool_list,
+PUHCI_TD ptd
+);
+
+BOOL
+uhci_init(
+PUHCI_DEV uhci,
+PADAPTER_OBJECT padapter
+);
+
+BOOL
+uhci_destroy(
+PUHCI_DEV uhci
+);
+
+// funcitons exported to dev-manager
+BOOL
+uhci_add_device(
+PUHCI_DEV uhci,
+PUSB_DEV dev
+);
+
+BOOL
+uhci_remove_device(
+PUHCI_DEV uhci,
+PUSB_DEV dev
+);
+
+//helpers
+static VOID
+uhci_dpc_callback(
+PKDPC dpc,
+PVOID context,
+PVOID sysarg1,
+PVOID sysarg2
+);
+
+static VOID
+uhci_flush_adapter_buf()
+{
+#ifdef _X86
+ __asm invd;
+#endif
+}
+
+BOOL
+uhci_submit_urb(
+PUHCI_DEV uhci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+struct _URB *urb
+);
+
+//must have dev_lock acquired
+static NTSTATUS
+uhci_internal_submit_bulk(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static NTSTATUS
+uhci_internal_submit_iso(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static NTSTATUS
+uhci_internal_submit_ctrl(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static NTSTATUS
+uhci_internal_submit_int(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static BOOL
+uhci_remove_bulk_from_schedule(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+#define uhci_remove_ctrl_from_schedule uhci_remove_bulk_from_schedule
+
+static BOOL
+uhci_remove_iso_from_schedule(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static BOOL
+uhci_remove_int_from_schedule(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static BOOL
+uhci_remove_urb_from_schedule(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static BOOL
+uhci_is_xfer_finished( //will set urb error code here
+struct _URB *urb
+);
+
+static NTSTATUS
+uhci_set_error_code(
+struct _URB *urb,
+ULONG raw_status
+);
+
+static BOOL
+uhci_insert_tds_qh(
+PUHCI_QH pqh,
+PUHCI_TD td_chain
+);
+
+static BOOL
+uhci_insert_qh_urb(
+struct _URB *urb,
+PUHCI_QH qh_chain
+);
+
+static BOOL
+uhci_insert_urb_schedule(
+PUHCI_DEV uhci,
+struct _URB *urb
+);
+
+static BOOL
+uhci_claim_bandwidth(
+PUHCI_DEV uhci,
+struct _URB *urb,
+BOOL claim_bw
+);
+
+static BOOL
+uhci_process_pending_endp(
+PUHCI_DEV uhci
+);
+
+NTSTATUS
+uhci_cancel_urb(
+PUHCI_DEV uhci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT endp,
+struct _URB *urb
+);
+
+VOID
+uhci_generic_urb_completion(
+struct _URB *urb,
+PVOID context
+);
+
+// the following are NT driver definitions
+
+// NT device name
+#define UHCI_DEVICE_NAME "\\Device\\UHCI"
+
+// File system device name. When you execute a CreateFile call to open the
+// device, use "\\.\GpdDev", or, given C's conversion of \\ to \, use
+// "\\\\.\\GpdDev"
+
+#define DOS_DEVICE_NAME "\\DosDevices\\UHCI"
+
+
+#define CLR_RH_PORTSTAT( port_idx, x ) \
+{\
+ PUSHORT addr; \
+ addr = ( PUSHORT )( uhci->port_base + port_idx ); \
+ status = READ_PORT_USHORT( addr ); \
+ status = ( status & 0xfff5 ) & ~( x ); \
+ WRITE_PORT_USHORT( addr, ( USHORT )status ); \
+}
+
+#define SET_RH_PORTSTAT( port_idx, x ) \
+{\
+ PUSHORT addr; \
+ addr = ( PUSHORT )( uhci->port_base + port_idx ); \
+ status = READ_PORT_USHORT( addr ); \
+ status = ( status & 0xfff5 ) | ( x ); \
+ WRITE_PORT_USHORT( addr, ( USHORT )status ); \
+}
+
+//this is for dispatch routine
+#define EXIT_DISPATCH( nTstatUs, iRp)\
+{\
+ if( nTstatUs != STATUS_PENDING)\
+ {\
+ iRp->IoStatus.Status = nTstatUs;\
+ IoCompleteRequest( iRp, IO_NO_INCREMENT);\
+ return nTstatUs;\
+ }\
+ IoMarkIrpPending( iRp);\
+ return nTstatUs;\
+}
+
+#endif
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/uhci.c b/reactos/drivers/usb/nt4compat/usbdriver/uhci.c
new file mode 100644
index 00000000000..9b19ea2de5a
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/uhci.c
@@ -0,0 +1,4222 @@
+/**
+ * uhci.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "uhciver.h"
+#include "td.h"
+#include "hub.h"
+#include "debug.h"
+#include "stdio.h"
+#include "usb.h"
+#include
+
+//----------------------------------------------------------
+// uhci routines
+//#define DEMO
+
+#ifdef INCLUDE_EHCI
+
+#define rh_port1_status rh_port_status[ 1 ]
+#define rh_port2_status rh_port_status[ 2 ]
+
+extern PDEVICE_OBJECT
+ehci_probe(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+PUSB_DEV_MANAGER dev_mgr
+);
+
+#endif
+
+#define DEFAULT_ENDP( enDP ) \
+( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP )
+
+#define dev_from_endp( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? ( ( PUSB_DEV )( enDP )->pusb_if )\
+ : ( ( enDP )->pusb_if->pusb_config->pusb_dev ) )
+
+#define endp_state( enDP ) ( ( enDP )->flags & USB_ENDP_FLAG_STAT_MASK )
+
+#define endp_num( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? 0 \
+ : ( ( enDP )->pusb_endp_desc->bEndpointAddress & 0x0f ) )
+
+#define endp_dir( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? 0L\
+ : ( ( enDP )->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) )
+
+#define dev_set_state( pdEV, staTE ) \
+( pdEV->flags = ( ( pdEV )->flags & ( ~USB_DEV_STATE_MASK ) ) | ( staTE ) )
+
+#define endp_max_packet_size( enDP ) \
+( DEFAULT_ENDP( enDP )\
+ ? ( ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc ? \
+ ( ( PUSB_DEV )enDP->pusb_if )->pusb_dev_desc->bMaxPacketSize0\
+ : 8 )\
+ : enDP->pusb_endp_desc->wMaxPacketSize )
+
+
+#define release_adapter( padapTER ) \
+{\
+ ( ( padapTER ) ); \
+}
+
+#define get_int_idx( _urb, _idx ) \
+{\
+ UCHAR interVAL;\
+ interVAL = ( UCHAR )( ( _urb )->pipe >> 24 );\
+ for( _idx = 1; _idx < 9; _idx++ )\
+ {\
+ interVAL >>= 1;\
+ if( !interVAL )\
+ break;\
+ }\
+ _idx --;\
+}
+
+#define uhci_insert_urb_to_schedule( uHCI, pURB, rET ) \
+{\
+ SYNC_PARAM sync_param;\
+ sync_param.uhci = uHCI;\
+ sync_param.context = pURB;\
+\
+ rET = KeSynchronizeExecution( uHCI->pdev_ext->uhci_int, uhci_sync_insert_urb_schedule, &sync_param );\
+}
+
+//declarations
+typedef struct
+{
+ PUHCI_DEV uhci;
+ PVOID context;
+ ULONG ret;
+
+} SYNC_PARAM, *PSYNC_PARAM;
+
+PDEVICE_OBJECT
+uhci_alloc(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+ULONG bus_addr,
+PUSB_DEV_MANAGER dev_mgr
+);
+
+BOOL
+uhci_init_schedule(
+PUHCI_DEV uhci,
+PADAPTER_OBJECT padapter
+);
+
+BOOL
+uhci_release(
+PDEVICE_OBJECT pdev
+);
+
+static VOID
+uhci_stop(
+PUHCI_DEV uhci
+);
+
+BOOL
+uhci_destroy_schedule(
+PUHCI_DEV uhci
+);
+
+BOOLEAN
+uhci_sync_insert_urb_schedule(
+PVOID context
+);
+
+VOID
+uhci_init_hcd_interface(
+PUHCI_DEV uhci
+);
+
+NTSTATUS
+uhci_rh_submit_urb(
+PUSB_DEV rh,
+PURB purb
+);
+
+NTSTATUS
+uhci_dispatch_irp(
+IN PDEVICE_OBJECT DeviceObject,
+IN PIRP irp
+);
+
+extern VOID
+rh_timer_svc_reset_port_completion(
+PUSB_DEV dev,
+PVOID context
+);
+
+extern VOID
+rh_timer_svc_int_completion(
+PUSB_DEV dev,
+PVOID context
+);
+
+ULONG debug_level = DBGLVL_MAXIMUM;
+PDRIVER_OBJECT usb_driver_obj = NULL;
+extern USB_DEV_MANAGER g_dev_mgr;
+
+//pending endpoint pool funcs
+VOID
+uhci_wait_ms(
+PUHCI_DEV uhci,
+LONG ms
+)
+{
+ LARGE_INTEGER lms;
+ if( ms <= 0 )
+ return;
+
+ lms.QuadPart = -10 * ms;
+ KeSetTimer( &uhci->reset_timer, lms, NULL );
+
+ KeWaitForSingleObject(
+ &uhci->reset_timer,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ return;
+}
+
+BOOL
+init_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+)
+{
+ int i;
+ if( pool == NULL )
+ return FALSE;
+
+ pool->pending_endp_array = usb_alloc_mem( NonPagedPool, sizeof( UHCI_PENDING_ENDP ) * UHCI_MAX_PENDING_ENDPS );
+ InitializeListHead( &pool->free_que );
+ pool->free_count = 0;
+ pool->total_count = UHCI_MAX_PENDING_ENDPS;
+ KeInitializeSpinLock( &pool->pool_lock );
+
+ for( i = 0; i < MAX_TIMER_SVCS; i++ )
+ {
+ free_pending_endp( pool, &pool->pending_endp_array[i] );
+ }
+
+ return TRUE;
+
+}
+
+BOOL
+free_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+PUHCI_PENDING_ENDP pending_endp
+)
+{
+ if( pool == NULL || pending_endp == NULL )
+ {
+ return FALSE;
+ }
+
+ RtlZeroMemory( pending_endp, sizeof( UHCI_PENDING_ENDP ) );
+ InsertTailList( &pool->free_que, (PLIST_ENTRY) &pending_endp->endp_link );
+ pool->free_count++;
+
+ return TRUE;
+}
+
+PUHCI_PENDING_ENDP
+alloc_pending_endp(
+PUHCI_PENDING_ENDP_POOL pool,
+LONG count)
+{
+ PUHCI_PENDING_ENDP new;
+ if( pool == NULL || count != 1 )
+ return NULL;
+
+ if( pool->free_count <= 0 )
+ return NULL;
+
+ new = ( PUHCI_PENDING_ENDP )RemoveHeadList( &pool->free_que );
+ pool->free_count --;
+ return new;
+}
+
+BOOL
+destroy_pending_endp_pool(
+PUHCI_PENDING_ENDP_POOL pool
+)
+{
+ if( pool == NULL )
+ return FALSE;
+
+ InitializeListHead( &pool->free_que );
+ pool->free_count = pool->total_count = 0;
+ usb_free_mem( pool->pending_endp_array );
+ pool->pending_endp_array = NULL;
+
+ return TRUE;
+
+}
+
+
+//end of pending endpoint pool funcs
+
+static void
+uhci_fill_td(
+PUHCI_TD td,
+ULONG status,
+ULONG info,
+ULONG buffer)
+{
+ td->status = status;
+ td->info = info;
+ td->buffer = buffer;
+}
+
+BOOL
+uhci_insert_td_fl(
+PUHCI_TD prev_td,
+PUHCI_TD ptd
+)
+{
+ ULONG temp;
+ PLIST_ENTRY temp_entry;
+
+ if( prev_td == NULL || ptd == NULL )
+ return FALSE;
+
+ temp_entry = &prev_td->ptde->hori_link;
+
+ ptd->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr;
+ prev_td->link = ptd->phy_addr;
+
+ InsertHeadList( &prev_td->ptde->hori_link, &ptd->ptde->hori_link );
+ return TRUE;
+}
+
+BOOL
+uhci_remove_td_fl(
+PUHCI_TD ptd
+)
+{
+ PUHCI_TD prev_td;
+
+ if( ptd == NULL )
+ return FALSE;
+
+ prev_td = ( struct_ptr( ptd->ptde->hori_link.Blink, TD_EXTENSION, hori_link ) )->ptd;
+ prev_td->link = ptd->link;
+ ptd->link = UHCI_PTR_TERM;
+
+ RemoveEntryList( &ptd->ptde->hori_link );
+
+ return FALSE;
+}
+
+BOOL
+uhci_insert_qh_fl(
+PVOID prev_item,
+PUHCI_QH pqh
+)
+{
+ //only horizontal link allowed
+ PUHCI_QH pprev_qh;
+ PUHCI_TD pprev_td;
+ ULONG temp;
+ PLIST_ENTRY temp_entry;
+
+ if( prev_item == NULL || pqh == NULL )
+ return FALSE;
+
+ if( ( ( (PUHCI_TD)prev_item )->ptde->flags & UHCI_ITEM_FLAG_TYPE )
+ == UHCI_ITEM_FLAG_QH )
+ {
+ pprev_qh = (PUHCI_QH)prev_item;
+ temp_entry = pprev_qh->pqhe->hori_link.Flink;
+ pqh->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr;
+ pprev_qh->link = pqh->phy_addr;
+
+ InsertHeadList( &pprev_qh->pqhe->hori_link, &pqh->pqhe->hori_link );
+ }
+ else
+ {
+ pprev_td = ( (PUHCI_TD)prev_item );
+
+ temp_entry = pprev_td->ptde->hori_link.Flink;
+ pprev_td->link = pqh->phy_addr;
+ pqh->link = ( struct_ptr( temp_entry, TD_EXTENSION, hori_link ) )->ptd->phy_addr;
+
+ InsertHeadList( &pprev_td->ptde->hori_link, &pqh->pqhe->hori_link );
+ }
+
+ return FALSE;
+}
+
+BOOL
+uhci_remove_qh_fl(
+PUHCI_QH pqh
+)
+{
+ PVOID prev_item;
+ PUHCI_QH pprevqh;
+ PUHCI_TD pprevtd;
+
+ if( pqh == NULL )
+ return FALSE;
+
+ prev_item = ( struct_ptr( pqh->pqhe->hori_link.Blink, TD_EXTENSION, hori_link ) )->ptd;
+
+ if( ( ( ( PUHCI_TD )prev_item )->ptde->flags & UHCI_ITEM_FLAG_TYPE )
+ == UHCI_ITEM_FLAG_QH )
+ {
+ pprevqh = (PUHCI_QH)prev_item;
+ pprevqh->link = pqh->link;
+ }
+ else
+ {
+ pprevtd = ( (PUHCI_TD)prev_item );
+ pprevtd->link = pqh->link;
+ }
+
+ RemoveEntryList( &pqh->pqhe->hori_link );
+
+ pqh->link = UHCI_PTR_TERM;
+ pqh->pqhe->hori_link.Flink = pqh->pqhe->hori_link.Blink = NULL;
+
+ return TRUE;
+}
+
+BOOL
+uhci_init_frame_list(
+PUHCI_DEV uhci,
+PADAPTER_OBJECT padapter
+)
+{
+ int i;
+ if( uhci == NULL || padapter == NULL )
+ return FALSE;
+
+ //note: frame_list_lock will be connected to interrupt
+ KeInitializeSpinLock( &uhci->frame_list_lock );
+
+ uhci->io_buf = \
+ HalAllocateCommonBuffer(
+ padapter,
+ 4096,
+ &uhci->io_buf_logic_addr,
+ FALSE);
+
+ if( uhci->io_buf == NULL )
+ return FALSE;
+
+ uhci->frame_list = \
+ HalAllocateCommonBuffer(
+ padapter,
+ sizeof(ULONG) * UHCI_MAX_FRAMES,
+ &uhci->frame_list_logic_addr,
+ FALSE);
+
+ if( uhci->frame_list == NULL )
+ return FALSE;
+
+ RtlZeroMemory( uhci->frame_list,
+ sizeof( ULONG ) * UHCI_MAX_FRAMES );
+
+ uhci->frame_list_cpu = usb_alloc_mem(
+ NonPagedPool,
+ sizeof( FRAME_LIST_CPU_ENTRY ) * UHCI_MAX_FRAMES );
+
+ if( uhci->frame_list_cpu == NULL )
+ return FALSE;
+
+ for( i = 0; i < UHCI_MAX_FRAMES; i++ )
+ InitializeListHead( &uhci->frame_list_cpu[ i ].td_link );
+
+ uhci->frame_bw = usb_alloc_mem( NonPagedPool,
+ sizeof( LONG ) * UHCI_MAX_FRAMES );
+
+ if( uhci->frame_bw == NULL )
+ return FALSE;
+
+ for( i = 0; i < UHCI_MAX_FRAMES; i++ )
+ {
+ uhci->frame_bw[ i ] = FRAME_TIME_MAX_USECS_ALLOC;
+ }
+ uhci->fsbr_cnt = 0;
+
+ return TRUE;
+
+}
+
+BOOL
+uhci_destroy_frame_list(
+PUHCI_DEV uhci
+)
+{
+
+ if( uhci == NULL )
+ return FALSE;
+
+ if( uhci->frame_list )
+ HalFreeCommonBuffer(
+ uhci->pdev_ext->padapter,
+ sizeof(ULONG) * UHCI_MAX_FRAMES,
+ uhci->frame_list_logic_addr,
+ uhci->frame_list,
+ FALSE );
+
+ uhci->frame_list = NULL;
+ uhci->frame_list_logic_addr.LowPart = 0;
+ uhci->frame_list_logic_addr.HighPart = 0;
+
+ if( uhci->frame_list_cpu )
+ usb_free_mem( uhci->frame_list_cpu );
+
+ uhci->frame_list_cpu = NULL;
+
+ if( uhci->frame_bw )
+ usb_free_mem( uhci->frame_bw );
+
+ uhci->frame_bw = NULL;
+
+ return TRUE;
+}
+
+PDEVICE_OBJECT
+uhci_create_device(
+PDRIVER_OBJECT drvr_obj,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ NTSTATUS status;
+ PDEVICE_OBJECT pdev;
+ PDEVICE_EXTENSION pdev_ext;
+
+ UNICODE_STRING dev_name;
+ UNICODE_STRING symb_name;
+
+ STRING string, another_string;
+ CHAR str_dev_name[ 64 ], str_symb_name[ 64 ];
+ UCHAR hcd_id;
+
+ if( drvr_obj == NULL )
+ return NULL;
+
+ ASSERT( dev_mgr != NULL );
+
+ //note: hcd count wont increment till the hcd is registered in dev_mgr
+ sprintf( str_dev_name, "%s%d", UHCI_DEVICE_NAME, dev_mgr->hcd_count );
+ sprintf( str_symb_name, "%s%d", DOS_DEVICE_NAME, dev_mgr->hcd_count );
+
+ RtlInitString( &string, str_dev_name );
+ RtlAnsiStringToUnicodeString( &dev_name, &string, TRUE );
+
+ pdev = NULL;
+ status = IoCreateDevice(
+ drvr_obj,
+ sizeof( DEVICE_EXTENSION ) + sizeof( UHCI_DEV ),
+ &dev_name,
+ FILE_UHCI_DEV_TYPE,
+ 0,
+ FALSE,
+ &pdev);
+
+ if( status != STATUS_SUCCESS || pdev == NULL )
+ {
+ RtlFreeUnicodeString( &dev_name );
+ return NULL;
+ }
+
+ pdev_ext = pdev->DeviceExtension;
+ RtlZeroMemory( pdev_ext,
+ sizeof( DEVICE_EXTENSION )
+ + sizeof( UHCI_DEV) );
+
+ pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_HCD;
+ pdev_ext->dev_ext_hdr.dispatch = uhci_dispatch_irp;
+ pdev_ext->dev_ext_hdr.start_io = NULL; //we do not support startio
+ pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
+
+ pdev_ext->pdev_obj = pdev;
+ pdev_ext->pdrvr_obj = drvr_obj;
+
+ pdev_ext->uhci = ( PUHCI_DEV ) &( pdev_ext[ 1 ] );
+
+ RtlInitString( &another_string, str_symb_name );
+ RtlAnsiStringToUnicodeString( &symb_name, &another_string, TRUE );
+
+ IoCreateSymbolicLink( &symb_name, &dev_name );
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_create_device(): dev=0x%x\n, pdev_ext= 0x%x, uhci=0x%x, dev_mgr=0x%x\n", \
+ pdev,\
+ pdev_ext,\
+ pdev_ext->uhci, \
+ dev_mgr ) );
+
+ RtlFreeUnicodeString( &dev_name );
+ RtlFreeUnicodeString( &symb_name );
+
+ //register with dev_mgr though it is not initilized
+ uhci_init_hcd_interface( pdev_ext->uhci );
+ hcd_id = dev_mgr_register_hcd( dev_mgr, &pdev_ext->uhci->hcd_interf );
+
+ pdev_ext->uhci->hcd_interf.hcd_set_id( &pdev_ext->uhci->hcd_interf, hcd_id );
+ pdev_ext->uhci->hcd_interf.hcd_set_dev_mgr( &pdev_ext->uhci->hcd_interf, dev_mgr );
+ return pdev;
+}
+
+BOOL
+uhci_delete_device(
+PDEVICE_OBJECT pdev
+)
+{
+ STRING string;
+ UNICODE_STRING symb_name;
+ PDEVICE_EXTENSION pdev_ext;
+ CHAR str_symb_name[ 64 ];
+
+
+ if( pdev == NULL )
+ return FALSE;
+
+ pdev_ext = pdev->DeviceExtension;
+
+ sprintf( str_symb_name,
+ "%s%d",
+ DOS_DEVICE_NAME,
+ pdev_ext->uhci->hcd_interf.hcd_get_id( &pdev_ext->uhci->hcd_interf ) );
+ RtlInitString( &string, str_symb_name );
+ RtlAnsiStringToUnicodeString( &symb_name, &string, TRUE );
+ IoDeleteSymbolicLink( &symb_name );
+ RtlFreeUnicodeString( &symb_name );
+
+ if( pdev_ext->res_list )
+ ExFreePool( pdev_ext->res_list ); // not allocated by usb_alloc_mem
+
+ IoDeleteDevice( pdev );
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_delete_device(): device deleted\n" ) );
+ return TRUE;
+}
+
+BOOLEAN
+uhci_isr(
+PKINTERRUPT interrupt,
+PVOID context
+)
+ // we can not use endp here for it is within the dev scope, and
+ // we can not acquire the dev-lock, fortunately we saved some
+ // info in urb->pipe in uhci_internal_submit_XXX.
+
+{
+
+ PUHCI_DEV uhci;
+ USHORT status;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+
+
+ /*
+ * Read the interrupt status, and write it back to clear the
+ * interrupt cause
+ */
+ uhci = ( PUHCI_DEV )context;
+ if( uhci == NULL )
+ return FALSE;
+
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBSTS ) );
+ if (!status) /* shared interrupt, not mine */
+ return FALSE;
+
+ if( status != 1 )
+ {
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_isr(): current uhci status=0x%x\n", status ) );
+ }
+ else
+ {
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_isr(): congratulations, no error occurs\n" ) );
+ }
+
+ /* clear it*/
+ WRITE_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBSTS ), status );
+
+ if ( status & ~( USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD ) )
+ {
+ if ( status & USBSTS_HSE )
+ {
+ DbgPrint( "uhci_isr(): host system error, PCI problems?\n");
+ //for( ; ; );
+ }
+ if ( status & USBSTS_HCPE )
+ {
+ DbgPrint( "uhci_isr(): host controller process error. something bad happened\n");
+ //for( ; ; );
+ //for( ; ; );
+ }
+ if ( ( status & USBSTS_HCH ) ) //&& !uhci->is_suspended
+ {
+ DbgPrint( "uhci_isr(): host controller halted. very bad\n");
+ /* FIXME: Reset the controller, fix the offending TD */
+ }
+ }
+
+ // don't no how to handle it yet
+ //if (status & USBSTS_RD)
+ //{
+ //uhci_wakeup(uhci);
+ //}*/
+
+ //let's remove those force-cancel urbs from the schedule first
+ ListFirst( &uhci->urb_list, pthis );
+ while( pthis )
+ {
+ purb = ( PURB )pthis;
+ if( purb->flags & URB_FLAG_FORCE_CANCEL )
+ {
+ uhci_remove_urb_from_schedule( uhci, purb );
+ }
+ ListNext( &uhci->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ //clear the interrupt if the urb is force canceled
+ uhci->skel_term_td->status &= ~TD_CTRL_IOC;
+
+ //next we need to find if anything fininshed
+ ListFirst( &uhci->urb_list, pthis );
+ while( pthis )
+ {
+ purb = ( PURB )pthis;
+ if( purb->flags & URB_FLAG_IN_SCHEDULE )
+ {
+ if( uhci_is_xfer_finished( purb ) )
+ uhci_remove_urb_from_schedule( uhci, purb );
+ }
+ ListNext( &uhci->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ KeInsertQueueDpc( &uhci->pdev_ext->uhci_dpc, uhci, 0 );
+ return TRUE;
+}
+
+BOOLEAN
+uhci_cal_cpu_freq(
+PVOID context
+)
+{
+ usb_cal_cpu_freq();
+ return TRUE;
+}
+
+PDEVICE_OBJECT
+uhci_probe(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+ LONG bus, i, j, ret;
+ PCI_SLOT_NUMBER slot_num;
+ PPCI_COMMON_CONFIG pci_config;
+ PDEVICE_OBJECT pdev;
+ BYTE buffer[ sizeof( PCI_COMMON_CONFIG ) ];
+ LONG count;
+ PDEVICE_EXTENSION pdev_ext;
+
+ slot_num.u.AsULONG = 0;
+ pci_config = ( PPCI_COMMON_CONFIG ) buffer;
+ count = 0;
+ pdev = NULL;
+
+ //scan the bus to find uhci controller
+ for( bus = 0; bus < 2; bus++ ) /*enum only bus0 and bus1*/
+ {
+ for( i = 0; i < PCI_MAX_DEVICES; i++ )
+ {
+ slot_num.u.bits.DeviceNumber = i;
+ for( j = 0; j < PCI_MAX_FUNCTIONS; j++ )
+ {
+ slot_num.u.bits.FunctionNumber = j;
+
+ ret = HalGetBusData(
+ PCIConfiguration,
+ bus,
+ slot_num.u.AsULONG,
+ pci_config,
+ PCI_COMMON_HDR_LENGTH );
+
+ if( ret == 0 ) /*no this bus*/
+ break;
+
+ if( ret == 2 ) /*no device on the slot*/
+ break;
+
+ if( pci_config->BaseClass == 0x0c && pci_config->SubClass == 0x03 )
+ {
+ // well, we find our usb host controller, create device
+#ifdef _MULTI_UHCI
+ {
+ pdev = uhci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr );
+ count++;
+ if( !pdev )
+ return NULL;
+ }
+#else
+ pdev = uhci_alloc( drvr_obj, reg_path, ( ( bus << 8 ) | ( i << 3 ) | j ), dev_mgr );
+ if( pdev )
+ goto LBL_LOOPOUT;
+#endif
+ }
+ }
+ if( ret == 0 )
+ break;
+ }
+ }
+LBL_LOOPOUT:
+ if( pdev )
+ {
+ pdev_ext = pdev->DeviceExtension;
+ if( pdev_ext )
+ {
+ // acquire higher irql to eliminate pre-empty
+ KeSynchronizeExecution( pdev_ext->uhci_int, uhci_cal_cpu_freq, NULL );
+ }
+ }
+ return NULL;
+}
+
+PDEVICE_OBJECT
+uhci_alloc(
+PDRIVER_OBJECT drvr_obj,
+PUNICODE_STRING reg_path,
+ULONG bus_addr,
+PUSB_DEV_MANAGER dev_mgr
+)
+{
+
+ LONG frd_num, prd_num;
+ UCHAR buffer[ PCI_COMMON_HDR_LENGTH ];
+ PDEVICE_OBJECT pdev;
+ PDEVICE_EXTENSION pdev_ext;
+ ULONG vector, addr_space;
+ LONG bus;
+ KIRQL irql;
+ KAFFINITY affinity;
+ UCHAR hcd_id;
+
+ DEVICE_DESCRIPTION dev_desc;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR *pprd;
+ PCI_SLOT_NUMBER slot_num;
+ NTSTATUS status;
+
+
+ pdev = uhci_create_device( drvr_obj, dev_mgr );
+ pdev_ext = pdev->DeviceExtension;
+
+ pdev_ext->pci_addr = bus_addr;
+ bus = ( bus_addr >> 8 );
+
+ slot_num.u.AsULONG = 0;
+ slot_num.u.bits.DeviceNumber = ( ( bus_addr & 0xff ) >> 3 );
+ slot_num.u.bits.FunctionNumber = ( bus_addr & 0x07 );
+
+ if( pdev == NULL )
+ return pdev;
+
+ //now create adapter object
+ RtlZeroMemory( &dev_desc, sizeof( dev_desc ) );
+
+ dev_desc.Version = DEVICE_DESCRIPTION_VERSION;
+ dev_desc.Master = TRUE;
+ dev_desc.ScatterGather = TRUE;
+ dev_desc.Dma32BitAddresses = TRUE;
+ dev_desc.BusNumber = bus;
+ dev_desc.InterfaceType = PCIBus;
+ dev_desc.MaximumLength = \
+ UHCI_MAX_POOL_TDS * sizeof( UHCI_TD ) * UHCI_MAX_TD_POOLS \
+ + sizeof( UHCI_QH ) * UHCI_MAX_POOL_QHS \
+ + sizeof( ULONG ) * UHCI_MAX_FRAMES;
+
+ pdev_ext->map_regs = 2; // UHCI_MAX_TD_POOLS +
+ //+ BYTES_TO_PAGES( ( UHCI_MAX_POOL_TDS * 64 ) * UHCI_MAX_TD_POOLS ) ;
+
+ pdev_ext->padapter = HalGetAdapter( &dev_desc, &pdev_ext->map_regs);
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_alloc(): padapter=0x%x\n", pdev_ext->padapter ) );
+ if( pdev_ext->padapter == NULL )
+ {
+ //fatal error
+ uhci_delete_device( pdev );
+ return NULL;
+ }
+
+ DbgPrint("uhci_alloc(): reg_path=0x%x, \n \
+ uhci_alloc(): PCIBus=0x%x, bus=0x%x, bus_addr=0x%x \n \
+ uhci_alloc(): slot_num=0x%x, &res_list=0x%x \n", \
+ ( DWORD )reg_path, \
+ ( DWORD )PCIBus, \
+ ( DWORD )bus, \
+ ( DWORD )bus_addr,\
+ ( DWORD )slot_num.u.AsULONG, \
+ ( DWORD )&pdev_ext->res_list );
+
+ //let's allocate resources for this device
+ DbgPrint( "uhci_alloc(): about to assign slot res\n" );
+ if( ( status = HalAssignSlotResources(
+ reg_path,
+ NULL, //no class name yet
+ drvr_obj,
+ NULL, //no support of another uhci controller
+ PCIBus,
+ bus,
+ slot_num.u.AsULONG,
+ &pdev_ext->res_list ) )
+ != STATUS_SUCCESS )
+ {
+ DbgPrint( "uhci_alloc(): error assign slot res, 0x%x\n", status );
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ uhci_delete_device( pdev );
+ return NULL;
+ }
+
+ //parse the resource list
+ for( frd_num = 0; frd_num < ( LONG )pdev_ext->res_list->Count; frd_num ++ )
+ {
+ for( prd_num = 0; prd_num < ( LONG )pdev_ext->res_list->List[ frd_num ].PartialResourceList.Count; prd_num ++ )
+ {
+ pprd = &pdev_ext->res_list->List[ frd_num ].PartialResourceList.PartialDescriptors[ prd_num ];
+ if( pprd->Type == CmResourceTypePort )
+ {
+ RtlCopyMemory( &pdev_ext->res_port, &pprd->u.Port, sizeof( pprd->u.Port ) );
+
+ }
+ else if( pprd->Type == CmResourceTypeInterrupt )
+ {
+ RtlCopyMemory( &pdev_ext->res_interrupt, &pprd->u.Interrupt, sizeof( pprd->u.Interrupt ) );
+ }
+ }
+ }
+
+ //for port, translate them to system address
+ addr_space = 1;
+ if( HalTranslateBusAddress(
+ PCIBus,
+ bus,
+ pdev_ext->res_port.Start,
+ &addr_space, //io space
+ &pdev_ext->uhci->uhci_reg_base
+ )
+ != ( BOOLEAN )TRUE )
+ {
+ DbgPrint( "uhci_alloc(): error, can not translate bus address\n" );
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ uhci_delete_device( pdev );
+ return NULL;
+ }
+
+ DbgPrint( "uhci_alloc(): address space=0x%x\n, reg_base=0x%x\n", \
+ addr_space, pdev_ext->uhci->uhci_reg_base.u.LowPart );
+
+ if( addr_space == 0 )
+ {
+ //port has been mapped to memory space
+ pdev_ext->uhci->port_mapped = TRUE;
+ pdev_ext->uhci->port_base = ( PBYTE )MmMapIoSpace(
+ pdev_ext->uhci->uhci_reg_base,
+ pdev_ext->res_port.Length,
+ FALSE );
+
+ //fatal error can not map the registers
+ if( pdev_ext->uhci->port_base == NULL )
+ {
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ uhci_delete_device( pdev );
+ return NULL;
+ }
+ }
+ else
+ {
+ //io space
+ pdev_ext->uhci->port_mapped = FALSE;
+ pdev_ext->uhci->port_base = ( PBYTE )pdev_ext->uhci->uhci_reg_base.LowPart;
+ }
+
+ //before we connect the interrupt, we have to init uhci
+ pdev_ext->uhci->fsbr_cnt = 0;
+ pdev_ext->uhci->pdev_ext = pdev_ext;
+
+ if( uhci_init_schedule( pdev_ext->uhci, pdev_ext->padapter ) == FALSE )
+ {
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+ uhci_delete_device( pdev );
+ return NULL;
+ }
+
+ InitializeListHead( &pdev_ext->uhci->urb_list );
+ KeInitializeSpinLock( &pdev_ext->uhci->pending_endp_list_lock );
+ InitializeListHead( &pdev_ext->uhci->pending_endp_list );
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_alloc(): pending_endp_list=0x%x\n", \
+ &pdev_ext->uhci->pending_endp_list ) );
+
+ init_pending_endp_pool( &pdev_ext->uhci->pending_endp_pool );
+ KeInitializeTimer( &pdev_ext->uhci->reset_timer );
+
+ vector = HalGetInterruptVector(
+ PCIBus,
+ bus,
+ pdev_ext->res_interrupt.level,
+ pdev_ext->res_interrupt.vector,
+ &irql,
+ &affinity);
+
+ //connect the interrupt
+ DbgPrint( "uhci_alloc(): the int=0x%x\n", vector );
+ if( IoConnectInterrupt(
+ &pdev_ext->uhci_int,
+ uhci_isr,
+ pdev_ext->uhci,
+ NULL, //&pdev_ext->uhci->frame_list_lock,
+ vector,
+ irql,
+ irql,
+ LevelSensitive,
+ TRUE, //share the vector
+ affinity,
+ FALSE ) //No float save
+ != STATUS_SUCCESS )
+ {
+ uhci_release( pdev );
+ return NULL;
+ }
+
+ KeInitializeDpc( &pdev_ext->uhci_dpc,
+ uhci_dpc_callback,
+ ( PVOID )pdev_ext->uhci );
+
+ return pdev;
+}
+
+BOOL
+uhci_release(
+PDEVICE_OBJECT pdev
+)
+{
+ PDEVICE_EXTENSION pdev_ext;
+ PUHCI_DEV uhci;
+ KIRQL disp_level;
+ KIRQL old_level;
+
+ if( pdev == NULL )
+ return FALSE;
+
+ pdev_ext = pdev->DeviceExtension;
+
+ if( pdev_ext == NULL )
+ return FALSE;
+
+ uhci = pdev_ext->uhci;
+ if( uhci == NULL )
+ return FALSE;
+
+ uhci_stop( uhci );
+ //pdev_ext->uhci->conn_count = 0;
+ pdev_ext->uhci->fsbr_cnt = 0;
+
+ if( pdev_ext->uhci_int )
+ {
+ IoDisconnectInterrupt( pdev_ext->uhci_int );
+ pdev_ext->uhci_int = NULL;
+ }
+ else
+ TRAP();
+ destroy_pending_endp_pool( &pdev_ext->uhci->pending_endp_pool );
+ //pdev_ext->uhci->pending_endp_pool = NULL;
+
+ uhci_destroy_schedule( uhci );
+
+ release_adapter( pdev_ext->padapter );
+ pdev_ext->padapter = NULL;
+
+ uhci_delete_device( pdev );
+
+ return FALSE;
+
+}
+
+BOOL
+uhci_start(
+PHCD hcd
+)
+{
+// send cmds to start the uhc
+// shamelessly copied from linux's uhci.c
+ PUHCI_DEV uhci;
+ PBYTE io_addr;
+ int timeout = 10000;
+
+ uhci = uhci_from_hcd( hcd );
+ io_addr = uhci->port_base;
+
+ /*
+ * Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_HCRESET );
+ while ( READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) ) & USBCMD_HCRESET )
+ {
+ if (!--timeout)
+ {
+ break;
+ }
+ }
+
+ /* Turn on all interrupts */
+ WRITE_PORT_USHORT(
+ ( PUSHORT )( io_addr + USBINTR ),
+ USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP
+ );
+
+ /* Start at frame 0 */
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBFRNUM ), 0 );
+ WRITE_PORT_ULONG( ( PULONG )( io_addr + USBFLBASEADD ), uhci->frame_list_logic_addr.LowPart );
+
+ /* Run and mark it configured with a 64-byte max packet */
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_RS | USBCMD_CF | USBCMD_MAXP );
+
+ DbgPrint( "uhci_start(): current uhci status=0x%x\n", uhci_status( uhci ) );
+
+ return TRUE;
+}
+
+VOID
+uhci_stop(
+PUHCI_DEV uhci
+)
+{
+ PBYTE io_addr = uhci->port_base;
+ // turn off all the interrupt
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBINTR ), 0 );
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 );
+}
+
+static VOID
+uhci_reset(
+PUHCI_DEV uhci
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PLIST_ENTRY pthis, pnext;
+ PBYTE io_addr = uhci->port_base;
+
+ uhci_stop( uhci );
+ /* Global reset for 50ms */
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_GRESET );
+ //uhci_wait_ms( uhci, 50 );
+ usb_wait_ms_dpc( 50 );
+
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 );
+ //uhci_wait_ms( uhci, 10 );
+ usb_wait_ms_dpc( 10 );
+}
+
+static VOID
+uhci_suspend(
+PUHCI_DEV uhci
+)
+{
+ PBYTE io_addr = uhci->port_base;
+
+ //uhci->is_suspended = 1;
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), USBCMD_EGSM );
+
+}
+
+static VOID
+uhci_wakeup(
+PUHCI_DEV uhci
+)
+{
+ PBYTE io_addr;
+ unsigned int status;
+
+ io_addr = uhci->port_base;
+
+ WRITE_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ), 0 );
+
+ /* wait for EOP to be sent */
+ status = READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) );
+ while (status & USBCMD_FGR)
+ status = READ_PORT_USHORT( ( PUSHORT )( io_addr + USBCMD ) );
+
+ //uhci->is_suspended = 0;
+
+ /* Run and mark it configured with a 64-byte max packet */
+ WRITE_PORT_USHORT(
+ ( PUSHORT )( io_addr + USBCMD ),
+ USBCMD_RS | USBCMD_CF | USBCMD_MAXP
+ );
+
+}
+
+BOOL
+uhci_init_schedule(
+PUHCI_DEV uhci,
+PADAPTER_OBJECT padapter
+)
+{
+ int i, irq;
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_init_schedule(): entering..., uhci=0x%x\n", uhci ) );
+ if( uhci == NULL || padapter == NULL )
+ return FALSE;
+
+ if( init_td_pool_list( &uhci->td_pool, padapter ) == FALSE )
+ {
+ return FALSE;
+ }
+ if( init_qh_pool( &uhci->qh_pool, padapter ) == FALSE )
+ {
+ return FALSE;
+ }
+
+ //since uhci is not started we can freely access all resources.
+ for( i = 0; i < UHCI_MAX_SKELTDS; i++ )
+ {
+ uhci->skel_td[ i ] = alloc_td( &uhci->td_pool );
+ uhci_fill_td(uhci->skel_td[ i ],
+ 0,
+ ( UHCI_NULL_DATA_SIZE << 21 )
+ | ( 0x7f << 8 ) | USB_PID_IN,
+ 0 );
+
+ if( i > 0 )
+ {
+ uhci->skel_td[ i ]->link = uhci->skel_td[ i - 1 ]->phy_addr;
+ }
+ }
+
+ /*for( i = UHCI_MAX_SKELTDS - 3; i >= 0; i-- )
+ {
+ InsertTailList( &uhci->skel_int256_td->ptde->hori_link,
+ &uhci->skel_td[ i ]->ptde->hori_link );
+ }*/
+
+ for( i = 0; i < UHCI_MAX_SKELQHS; i++ )
+ {
+ uhci->skel_qh[ i ] = alloc_qh( &uhci->qh_pool );
+ if( i > 0 )
+ {
+ uhci->skel_qh[ i - 1 ]->link = uhci->skel_qh[ i ]->phy_addr;
+ }
+
+ uhci->skel_qh[ i ]->element = UHCI_PTR_TERM;
+ }
+
+ uhci->skel_int1_td->link = uhci->skel_ls_control_qh->phy_addr;
+
+ uhci_fill_td( uhci->skel_term_td, 0, ( UHCI_NULL_DATA_SIZE << 21 ) | ( 0x7f << 8 ) | USB_PID_IN, 0 );
+ uhci->skel_term_td->link = uhci->skel_term_td->phy_addr;
+
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ uhci->skel_term_qh->element = uhci->skel_term_td->phy_addr;
+
+ InsertTailList( &uhci->skel_term_qh->pqhe->vert_link, &uhci->skel_term_td->ptde->vert_link );
+
+ /*for( i = 0; i < UHCI_MAX_SKELQHS; i++ )
+ {
+ InsertTailList( &uhci->skel_int256_td->ptde->hori_link,
+ &uhci->skel_qh[ i ]->pqhe->hori_link );
+ }*/
+
+ if( uhci_init_frame_list( uhci, uhci->pdev_ext->padapter ) == FALSE )
+ uhci_destroy_frame_list( uhci );
+
+ //well all have been chained, now scatter the int tds to frame-list
+ //shamelessly pasted from linux's uhci.c :-)
+ for( i = 0; i < UHCI_MAX_FRAMES; i++ )
+ {
+ irq = 0;
+ if (i & 1)
+ {
+ irq++;
+ if (i & 2)
+ {
+ irq++;
+ if (i & 4)
+ {
+ irq++;
+ if (i & 8)
+ {
+ irq++;
+ if (i & 16)
+ {
+ irq++;
+ if (i & 32)
+ {
+ irq++;
+ if (i & 64)
+ irq++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Only place we don't use the frame list routines */
+ uhci->frame_list[ i ] = uhci->skel_td[ irq ]->phy_addr;
+ }
+ return TRUE;
+}
+
+BOOL
+uhci_destroy_schedule(
+PUHCI_DEV uhci
+)
+{
+ BOOL ret;
+
+ ret = uhci_destroy_frame_list( uhci );
+ ret = destroy_qh_pool( &uhci->qh_pool );
+ ret = destroy_td_pool_list( &uhci->td_pool );
+
+ return ret;
+
+}
+
+static VOID
+uhci_cancel_pending_endp_urb(
+IN PVOID Parameter
+)
+{
+ PLIST_ENTRY abort_list;
+ PUSB_DEV pdev;
+ PURB purb;
+ USE_IRQL;
+
+ abort_list = ( PLIST_ENTRY )Parameter;
+
+ if( abort_list == NULL )
+ return;
+
+ while( IsListEmpty( abort_list ) == FALSE )
+ {
+ //these devs are protected by urb's ref-count
+ purb = ( PURB )RemoveHeadList( abort_list );
+ pdev = purb->pdev;
+ // purb->status is set when they are added to abort_list
+
+ uhci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, FALSE );
+ pdev->ref_count--;
+ unlock_dev( pdev, FALSE );
+ }
+ usb_free_mem( abort_list );
+ return;
+}
+
+static BOOL
+uhci_process_pending_endp(
+PUHCI_DEV uhci
+)
+{
+ PUSB_DEV pdev;
+ LIST_ENTRY temp_list, abort_list;
+ PLIST_ENTRY pthis;
+ PURB purb;
+ PUSB_ENDPOINT pendp;
+ BOOL can_submit;
+ PWORK_QUEUE_ITEM pwork_item;
+ PLIST_ENTRY cancel_list;
+ USE_IRQL;
+
+ if( uhci == NULL )
+ return FALSE;
+
+ InitializeListHead( &temp_list );
+ InitializeListHead( &abort_list );
+
+ purb = NULL;
+ uhci_dbg_print( DBGLVL_MEDIUM, ("uhci_process_pending_endp(): entering..., uhci=0x%x\n", uhci ) );
+
+ lock_pending_endp_list( &uhci->pending_endp_list_lock );
+ while( IsListEmpty( &uhci->pending_endp_list ) == FALSE )
+ {
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_process_pending_endp(): pending_endp_list=0x%x\n", \
+ &uhci->pending_endp_list ) );
+
+ pthis = RemoveHeadList( &uhci->pending_endp_list );
+ pendp = ( ( PUHCI_PENDING_ENDP )pthis )->pendp;
+ pdev = dev_from_endp( pendp );
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ //delegate to uhci_remove_device for remiving the urb queue on the endpoint
+ continue;
+ }
+
+ if( endp_state( pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ while( IsListEmpty( &pendp->urb_list ) == FALSE )
+ {
+ purb = ( PURB )RemoveHeadList( &pendp->urb_list );
+ purb->status = USB_STATUS_ENDPOINT_HALTED;
+ InsertTailList( &abort_list, ( LIST_ENTRY* )purb );
+ }
+ InitializeListHead( &pendp->urb_list );
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ continue;
+ }
+
+
+ if( IsListEmpty( &pendp->urb_list ) == FALSE )
+ {
+ purb = ( PURB )RemoveHeadList( &pendp->urb_list );
+ ASSERT( purb );
+ }
+ else
+ {
+ InitializeListHead( &pendp->urb_list );
+ unlock_dev( pdev, TRUE );
+ free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ continue;
+ }
+
+ // if can_submit is STATUS_SUCCESS, the purb is inserted into the schedule
+ switch( endp_type( pendp ) )
+ {
+ case USB_ENDPOINT_XFER_BULK:
+ {
+#ifdef DEMO
+ can_submit = STATUS_UNSUCCESSFUL;
+#else
+ can_submit = uhci_internal_submit_bulk( uhci, purb );
+#endif
+ break;
+ }
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ can_submit = uhci_internal_submit_ctrl( uhci, purb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ can_submit = uhci_internal_submit_int( uhci, purb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_ISOC:
+ {
+ can_submit = uhci_internal_submit_iso( uhci, purb );
+ break;
+ }
+ }
+
+ if( can_submit == STATUS_NO_MORE_ENTRIES )
+ {
+ //no enough bandwidth or tds
+ InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb );
+ InsertTailList( &temp_list, pthis );
+ }
+ else
+ {
+ // other error or success
+ free_pending_endp(
+ &uhci->pending_endp_pool,
+ struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+
+ if( can_submit != STATUS_SUCCESS )
+ {
+ //abort these URBs
+ InsertTailList( &abort_list, ( LIST_ENTRY* )purb );
+ purb->status = can_submit;
+ }
+
+ }
+ unlock_dev( pdev, TRUE );
+ }
+
+ if( IsListEmpty( &temp_list ) == FALSE )
+ {
+ //re-append them to the pending_endp_list
+ ListFirst( &temp_list, pthis );
+ RemoveEntryList( &temp_list );
+ MergeList( &uhci->pending_endp_list, pthis );
+ }
+ unlock_pending_endp_list( &uhci->pending_endp_list_lock );
+
+ if( IsListEmpty( &abort_list ) == FALSE )
+ {
+ PLIST_ENTRY pthis;
+ cancel_list = ( PLIST_ENTRY )usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( LIST_ENTRY ) );
+ ASSERT( cancel_list );
+
+ ListFirst( &abort_list, pthis );
+ RemoveEntryList( &abort_list );
+ InsertTailList( pthis, cancel_list );
+
+ pwork_item = ( PWORK_QUEUE_ITEM )&cancel_list[ 1 ];
+
+ // we do not need to worry the uhci_cancel_pending_endp_urb running when the
+ // driver is unloading since it will prevent the dev_mgr to quit till all the
+ // reference count to the dev drop to zero.
+ ExInitializeWorkItem( pwork_item, uhci_cancel_pending_endp_urb, ( PVOID )cancel_list );
+ ExQueueWorkItem( pwork_item, DelayedWorkQueue );
+ }
+ return TRUE;
+}
+
+BOOL
+uhci_submit_urb(
+PUHCI_DEV uhci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+{
+ int i;
+ PLIST_ENTRY pthis, pnext;
+ PUHCI_PENDING_ENDP pending_endp;
+ NTSTATUS status;
+ USE_IRQL;
+
+ if( uhci == NULL || pdev == NULL || pendp == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_pending_endp_list( &uhci->pending_endp_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ status = purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto LBL_OUT;
+ }
+
+ if( dev_class( pdev ) == USB_DEV_CLASS_ROOT_HUB )
+ {
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &uhci->pending_endp_list_lock );
+ status = uhci_rh_submit_urb( pdev, purb );
+ return status;
+ }
+
+ if( pendp )
+ purb->pendp = pendp;
+ else
+ purb->pendp = &pdev->default_endp;
+
+ if( dev_from_endp( purb->pendp ) != pdev )
+ {
+ status = purb->status = STATUS_INVALID_PARAMETER;
+ goto LBL_OUT;
+ }
+
+ if( endp_state( purb->pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ status = purb->status = USB_STATUS_ENDPOINT_HALTED;
+ goto LBL_OUT;
+ }
+
+ purb->pdev = pdev;
+ purb->rest_bytes = purb->data_length;
+
+ if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_BULK )
+ purb->bytes_to_transfer =
+ ( purb->data_length
+ > purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER
+ ? purb->pendp->pusb_endp_desc->wMaxPacketSize * UHCI_MAX_TDS_PER_TRANSFER
+ : purb->data_length ); //multiple transfer for large data block
+ else
+ purb->bytes_to_transfer = purb->data_length;
+
+ uhci_dbg_print( DBGLVL_MEDIUM, ( "uhci_submit_urb(): bytes_to_transfer=0x%x\n", purb->bytes_to_transfer ) );
+
+ purb->bytes_transfered = 0;
+ InitializeListHead( &purb->trasac_list );
+ purb->last_finished_td = &purb->trasac_list;
+ purb->flags &= ~( URB_FLAG_STATE_MASK | URB_FLAG_IN_SCHEDULE | URB_FLAG_FORCE_CANCEL );
+ purb->flags |= URB_FLAG_STATE_PENDING;
+
+
+ i = IsListEmpty( &pendp->urb_list );
+ InsertTailList( &pendp->urb_list, &purb->urb_link );
+
+ pdev->ref_count ++; //for urb reference
+
+ if( i == FALSE )
+ {
+ //there is urb pending, simply queue it and return
+ status = purb->status = STATUS_PENDING;
+ goto LBL_OUT;
+ }
+ else if( usb_endp_busy_count( purb->pendp )
+ && endp_type( purb->pendp) != USB_ENDPOINT_XFER_ISOC )
+ {
+ //
+ //No urb waiting but urb overlap not allowed,
+ //so leave it in queue and return, will be scheduled
+ //later
+ //
+ status = purb->status = STATUS_PENDING;
+ goto LBL_OUT;
+ }
+
+ pending_endp = alloc_pending_endp( &uhci->pending_endp_pool, 1 );
+ if( pending_endp == NULL )
+ {
+ //panic
+ status = purb->status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT2;
+ }
+
+ pending_endp->pendp = purb->pendp;
+ InsertTailList( &uhci->pending_endp_list, ( PLIST_ENTRY )pending_endp );
+
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &uhci->pending_endp_list_lock );
+
+ uhci_process_pending_endp( uhci );
+ return STATUS_PENDING;
+
+LBL_OUT2:
+ pdev->ref_count --;
+ RemoveEntryList( ( PLIST_ENTRY )purb );
+
+LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ unlock_pending_endp_list( &uhci->pending_endp_list_lock );
+ return status;
+}
+
+static NTSTATUS
+uhci_set_error_code(
+PURB urb,
+ULONG raw_status
+)
+{
+
+ if( ( raw_status & TD_CTRL_ANY_ERROR ) == 0 )
+ {
+ //test if the urb is canceled
+ if( urb->flags & URB_FLAG_FORCE_CANCEL )
+ urb->status = STATUS_CANCELLED;
+ else
+ urb->status = STATUS_SUCCESS;
+ }
+
+ else if( raw_status & TD_CTRL_BABBLE )
+ urb->status = USB_STATUS_DATA_OVERRUN;
+
+ else if( raw_status & TD_CTRL_STALLED )
+ urb->status = USB_STATUS_STALL_PID;
+
+ else if( raw_status & TD_CTRL_DBUFERR )
+ urb->status = USB_STATUS_BUFFER_OVERRUN;
+
+ else if( raw_status & TD_CTRL_CRCTIMEO )
+ urb->status = USB_STATUS_CRC;
+
+ else if( raw_status & TD_CTRL_BITSTUFF )
+ urb->status = USB_STATUS_BTSTUFF;
+
+ else
+ urb->status = STATUS_UNSUCCESSFUL;
+
+ return urb->status;
+}
+
+BOOLEAN
+uhci_sync_remove_urb_finished(
+PVOID context
+)
+{
+ PUHCI_DEV uhci;
+ PLIST_ENTRY pthis, pnext, ptemp;
+ PURB purb;
+ PSYNC_PARAM pparam;
+
+ pparam = ( PSYNC_PARAM )context;
+ uhci = pparam->uhci;
+ ptemp = ( PLIST_ENTRY )pparam->context;
+
+ if( uhci == NULL )
+ {
+ return ( UCHAR )pparam->ret = FALSE;
+ }
+
+ ListFirst( &uhci->urb_list, pthis );
+ while( pthis )
+ {
+ //remove urbs not in the schedule
+ ListNext( &uhci->urb_list, pthis, pnext );
+ purb = ( PURB )pthis;
+
+ if( ( purb->flags & URB_FLAG_IN_SCHEDULE ) == 0 )
+ {
+ //finished or canceled( not apply for split bulk ).
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_FINISHED;
+ RemoveEntryList( pthis );
+ InsertTailList( ptemp, pthis );
+ }
+ pthis = pnext;
+ }
+ pparam->ret = TRUE;
+ return ( UCHAR )TRUE;
+}
+
+BOOLEAN
+uhci_drop_fsbr(
+PUHCI_DEV uhci
+)
+{
+
+ if( uhci == NULL )
+ return ( UCHAR )FALSE;
+
+ uhci->fsbr_cnt--;
+
+ if( uhci->fsbr_cnt <= 0 )
+ {
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+ uhci->fsbr_cnt = 0;
+ }
+
+ return ( UCHAR )TRUE;
+}
+VOID
+uhci_dpc_callback(
+PKDPC dpc,
+PVOID context,
+PVOID sysarg1,
+PVOID sysarg2
+)
+{
+ PUHCI_DEV uhci;
+
+ LIST_HEAD temp_list;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ PQH_EXTENSION pqhe;
+ PTD_EXTENSION ptde;
+ PUHCI_PENDING_ENDP pending_endp;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+
+ BOOL finished;
+ LONG i, j;
+ ULONG uhci_status, urb_status, toggle = 0;
+
+ SYNC_PARAM sync_param;
+ USE_IRQL;
+
+ uhci = ( PUHCI_DEV ) context;
+ if( uhci == NULL )
+ return;
+
+ uhci_status = ( ULONG )sysarg1;
+
+ InitializeListHead( &temp_list );
+
+ sync_param.uhci = uhci;
+ sync_param.context = ( PVOID )&temp_list;
+
+ uhci_dbg_print( DBGLVL_MAXIMUM, ( "uhci_dpc_callback(): entering..., uhci=0x%x\n", uhci ) );
+ //remove finished urb from uhci's urb-list
+ KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_remove_urb_finished, &sync_param );
+
+ //release resources( tds, and qhs ) the urb occupied
+ while( IsListEmpty( &temp_list ) == FALSE )
+ {
+ //not in any public queue, if do not access into dev, no race
+ //condition will occur
+ purb = ( PURB ) RemoveHeadList( &temp_list );
+ urb_status = purb->status;
+
+ //the only place we do not use this lock on non-pending-endp-list data ops
+ KeAcquireSpinLockAtDpcLevel( &uhci->pending_endp_list_lock );
+ while( IsListEmpty( &purb->trasac_list ) == FALSE )
+ {
+ pthis = RemoveHeadList( &purb->trasac_list );
+
+ if( ( ( ( PTD_EXTENSION )pthis )->flags & UHCI_ITEM_FLAG_TYPE )
+ == UHCI_ITEM_FLAG_QH )
+ {
+ pqhe = ( PQH_EXTENSION ) pthis;
+ lock_qh_pool( &uhci->qh_pool, TRUE );
+ free_qh( &uhci->qh_pool, pqhe->pqh );
+ unlock_qh_pool( &uhci->qh_pool, TRUE );
+ }
+ else
+ {
+ //must be a td chain
+ InsertHeadList( &purb->trasac_list, pthis );
+ for( i = 0, purb->bytes_transfered = 0; i < purb->td_count; i++ )
+ {
+ PUHCI_TD ptd;
+ // accumulate data transfered in tds
+ ptd = ( ( PTD_EXTENSION )pthis )->ptd;
+ if( ( ptd->status & TD_CTRL_ACTIVE ) == 0 && ( ptd->status & TD_CTRL_ANY_ERROR ) == 0 )
+ {
+ j = ptd->status & 0x7ff;
+ purb->bytes_transfered += ( ( j == 0x7ff ) ? 0 : ( j + 1 ) );
+
+ }
+ ListNext( &purb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ if( urb_status & TD_CTRL_ANY_ERROR )
+ {
+ if( purb->last_finished_td != NULL && purb->last_finished_td != &purb->trasac_list )
+ toggle = ( ( ( PTD_EXTENSION )purb->last_finished_td )->ptd->info & ( 1 << 19 ) );
+ }
+ //trick, remove trasac_list
+ ListFirst( &purb->trasac_list, pthis );
+ RemoveEntryList( &purb->trasac_list );
+ lock_td_pool( &uhci->td_pool, TRUE );
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ //termination condition
+ InitializeListHead( &purb->trasac_list );
+ purb->last_finished_td = NULL;
+ }
+ }
+
+ if( endp_type( purb->pendp ) == USB_ENDPOINT_XFER_ISOC
+ || endp_type( purb->pendp ) == USB_ENDPOINT_XFER_INT )
+ uhci_claim_bandwidth( uhci, purb, FALSE); //release band-width
+
+ KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock );
+
+ uhci_set_error_code( purb, urb_status );
+
+ finished = TRUE;
+
+ //since the ref_count for the urb is not released, we can safely have one
+ //pointer to dev
+ pdev = dev_from_endp( purb->pendp );
+ pendp = purb->pendp;
+
+ if( purb->status == USB_STATUS_BABBLE_DETECTED )
+ {
+ usb_dbg_print( DBGLVL_MEDIUM, ( "uhci_dpc_callback(): alert!!!, babble detected, severe error, reset the whole bus\n" ) );
+ uhci_reset( uhci );
+ uhci_start( &uhci->hcd_interf );
+ }
+
+ //this will let the new request in uhci_generic_urb_completion to this endp
+ //be processed rather than queued in the pending_endp_list
+ lock_dev( pdev, TRUE );
+ usb_endp_busy_count_dec( pendp );
+ unlock_dev( pdev, TRUE );
+
+ if( usb_success( purb->status ) == FALSE )
+ {
+ // set error code and complete the urb and purb is invalid from this point
+ uhci_generic_urb_completion( purb, purb->context );
+ }
+ else
+ {
+ if( ( purb->pipe & USB_ENDPOINT_XFERTYPE_MASK )
+ == USB_ENDPOINT_XFER_BULK )
+ {
+ purb->rest_bytes -= purb->bytes_transfered;
+ if( purb->rest_bytes )
+ {
+ finished = FALSE;
+ }
+ else
+ {
+ uhci_generic_urb_completion( purb, purb->context );
+ }
+ }
+ else
+ {
+ uhci_generic_urb_completion( purb, purb->context );
+ //purb is now invalid
+ }
+ }
+
+ KeAcquireSpinLockAtDpcLevel( &uhci->pending_endp_list_lock );
+ lock_dev( pdev, TRUE );
+
+ if( finished )
+ pdev->ref_count--;
+
+ if( urb_status & TD_CTRL_ANY_ERROR
+ && endp_type( pendp ) != USB_ENDPOINT_XFER_CONTROL )
+ {
+ pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
+ pendp->flags |= USB_ENDP_FLAG_STALL;
+ }
+
+ if( dev_state( pdev )== USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock );
+ if( finished == FALSE )
+ {
+
+ purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ uhci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, TRUE );
+ pdev->ref_count--;
+ unlock_dev( pdev, TRUE);
+ }
+ continue;
+ }
+
+ if( finished && IsListEmpty( &pendp->urb_list ) == TRUE )
+ {
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock );
+ continue;
+ }
+ else if( finished == TRUE )
+ {
+ //has urb in the endp's urb-list
+ if( usb_endp_busy_count( pendp ) > 0 )
+ {
+ //the urbs still have chance to be sheduled but not this time
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock );
+ continue;
+ }
+ }
+
+ if( finished == FALSE )
+ {
+ //a split bulk transfer
+ purb->bytes_transfered = 0;
+ purb->bytes_to_transfer =
+ UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize
+ > purb->rest_bytes
+ ? purb->rest_bytes
+ : UHCI_MAX_TDS_PER_TRANSFER * purb->pendp->pusb_endp_desc->wMaxPacketSize;
+
+ //the urb is not finished
+ purb->flags &= ~URB_FLAG_STATE_MASK;
+ purb->flags |= URB_FLAG_STATE_PENDING;
+
+ InsertHeadList( &pendp->urb_list, ( PLIST_ENTRY )purb );
+ }
+
+ pending_endp = alloc_pending_endp( &uhci->pending_endp_pool, 1 );
+ pending_endp->pendp = pendp;
+ InsertTailList( &uhci->pending_endp_list, &pending_endp->endp_link );
+
+ unlock_dev( pdev, TRUE);
+ KeReleaseSpinLockFromDpcLevel( &uhci->pending_endp_list_lock );
+ }
+
+ //ah...exhausted, let's find some in the pending_endp_list to rock
+ uhci_process_pending_endp( uhci );
+ return;
+}
+
+BOOL
+uhci_add_device(
+PUHCI_DEV uhci,
+PUSB_DEV dev
+)
+{
+ if( dev == NULL || uhci == NULL )
+ return FALSE;
+
+ return TRUE;
+
+}
+
+BOOLEAN
+uhci_sync_cancel_urbs_dev(
+PVOID context
+)
+{
+ //cancel all the urbs on one dev
+ PUHCI_DEV uhci;
+ PUSB_DEV pdev, dest_dev;
+ PSYNC_PARAM sync_param;
+ PLIST_ENTRY pthis, pnext;
+ LONG count;
+
+ sync_param = ( PSYNC_PARAM )context;
+ dest_dev = ( PUSB_DEV )sync_param->context;
+ uhci = sync_param->uhci;
+
+ if( uhci == NULL || dest_dev == NULL )
+ {
+ return ( UCHAR )sync_param->ret = FALSE;
+ }
+ count = 0;
+ ListFirst( &uhci->urb_list, pthis );
+ while( pthis )
+ {
+ pdev = dev_from_endp( ( ( PURB ) pthis )->pendp );
+ if( pdev == dest_dev )
+ {
+ ( ( PURB ) pthis )->flags |= URB_FLAG_FORCE_CANCEL;
+ }
+ ListNext( &uhci->urb_list, pthis, pnext );
+ pthis = pnext;
+ count ++;
+ }
+ if( count )
+ uhci->skel_term_td->status |= TD_CTRL_IOC;
+
+ return ( UCHAR )sync_param->ret = TRUE;
+}
+
+BOOL
+uhci_remove_device(
+PUHCI_DEV uhci,
+PUSB_DEV dev
+)
+{
+ PUHCI_PENDING_ENDP ppending_endp;
+ PLIST_ENTRY pthis, pnext;
+ PURB purb;
+ LIST_HEAD temp_list;
+ int i, j, k;
+ SYNC_PARAM sync_param;
+
+ USE_IRQL;
+
+ if( uhci == NULL || dev == NULL )
+ return FALSE;
+
+ InitializeListHead( &temp_list );
+
+ //free pending endp that has urb queued from pending endp list
+ lock_pending_endp_list( &uhci->pending_endp_list_lock );
+
+ ListFirst( &uhci->pending_endp_list, pthis );
+
+ while( pthis )
+ {
+ ppending_endp = ( PUHCI_PENDING_ENDP ) pthis;
+ ListNext( &uhci->pending_endp_list, pthis, pnext );
+ if( dev_from_endp( ppending_endp->pendp ) == dev )
+ {
+ RemoveEntryList( pthis );
+ free_pending_endp( &uhci->pending_endp_pool, struct_ptr( pthis, UHCI_PENDING_ENDP, endp_link ) );
+ }
+ pthis = pnext;
+ }
+ unlock_pending_endp_list( &uhci->pending_endp_list_lock );
+
+ //cancel all the urbs in the urb-list
+ sync_param.uhci = uhci;
+ sync_param.context = ( PVOID )dev;
+
+ KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_cancel_urbs_dev, &sync_param );
+
+ //cancel all the urb in the endp's urb-list
+ k = 0;
+ lock_dev( dev, FALSE );
+ if( dev->usb_config )
+ {
+ //only for configed dev
+ for( i = 0; i < dev->usb_config->if_count; i++ )
+ {
+ for( j = 0; j < dev->usb_config->interf[ i ].endp_count; j++ )
+ {
+ ListFirst( &dev->usb_config->interf[ i ].endp[ j ].urb_list, pthis );
+ while( pthis )
+ {
+ ListNext( &dev->usb_config->interf[ i ].endp[ j ].urb_list,
+ pthis,
+ pnext );
+
+ RemoveEntryList( pthis );
+ InsertHeadList( &temp_list, pthis );
+ pthis = pnext;
+ k++;
+ }
+
+ }
+ }
+ }
+ ListFirst( &dev->default_endp.urb_list, pthis );
+
+ while( pthis )
+ {
+ ListNext( &dev->default_endp.urb_list,
+ pthis,
+ pnext );
+
+ RemoveEntryList( pthis );
+ InsertHeadList( &temp_list, pthis );
+ pthis = pnext;
+ k++;
+ }
+ unlock_dev( dev, FALSE );
+
+ if( IsListEmpty( &temp_list ) == FALSE )
+ {
+ for( i = 0; i < k ; i++ )
+ {
+ //complete those urbs with error
+ pthis = RemoveHeadList( &temp_list );
+ purb = ( PURB ) pthis;
+ purb->status = STATUS_DEVICE_DOES_NOT_EXIST;
+ {
+ uhci_generic_urb_completion( purb, purb->context );
+ }
+ }
+ }
+
+ lock_dev( dev, FALSE )
+ dev->ref_count -= k;
+ unlock_dev( dev, FALSE );
+
+ return TRUE;
+}
+
+
+static NTSTATUS
+uhci_internal_submit_bulk(
+PUHCI_DEV uhci,
+PURB urb
+)
+//
+// assume that the urb has its rest_bytes and bytes_to_transfer set
+// and bytes_transfered is zeroed.
+// dev_lock must be acquired outside
+// urb comes from dev's endpoint urb-list. it is already removed from
+// the endpoint urb-list.
+//
+{
+
+ LONG max_packet_size, td_count, offset, bytes_to_transfer, data_load;
+ PBYTE start_addr;
+ PUHCI_TD ptd;
+ PUHCI_QH pqh;
+ LIST_ENTRY td_list, *pthis, *pnext;
+ BOOL old_toggle, toggle, ret;
+ UCHAR pid;
+
+ if( uhci == NULL || urb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ max_packet_size = endp_max_packet_size( urb->pendp );
+ if( urb->bytes_to_transfer == 0 )
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ td_count = ( urb->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size;
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ if( can_transfer( &uhci->td_pool, td_count ) == FALSE )
+ {
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ ptd = alloc_tds( &uhci->td_pool, td_count );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ if( ptd == NULL )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ InitializeListHead( &td_list );
+ InsertTailList( &ptd->ptde->vert_link, &td_list );
+
+ ListFirst( &td_list, pthis );
+ ListNext( &td_list, pthis, pnext );
+
+ start_addr = &urb->data_buffer[ urb->data_length - urb->rest_bytes ];
+ offset = 0;
+
+ old_toggle = toggle = urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ? TRUE : FALSE;
+ bytes_to_transfer = urb->bytes_to_transfer;
+
+ urb->pipe = ( ( max_packet_size - 1 ) << 21 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( dev_from_endp( urb->pendp )->dev_addr << 8 )
+ | ( ( ULONG )endp_dir( urb->pendp ) )
+ | USB_ENDPOINT_XFER_BULK;
+
+ pid = ( ( ( ULONG )urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN ) ? USB_PID_IN : USB_PID_OUT );
+ while( pthis )
+ {
+ ptd = ( ( PTD_EXTENSION )pthis )->ptd;
+
+ data_load = max_packet_size < bytes_to_transfer ? max_packet_size : bytes_to_transfer;
+ ptd->purb = urb;
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT )
+ | ( TD_CTRL_ACTIVE ),
+ ( ( data_load - 1 ) << 21 )
+ | ( toggle << 19 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( dev_from_endp( urb->pendp )->dev_addr << 8 )
+ | pid,
+ MmGetPhysicalAddress( start_addr + offset ).LowPart );
+
+ bytes_to_transfer -= data_load;
+ offset += data_load;
+
+ if( pnext )
+ {
+ ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr;
+ }
+ else
+ {
+ //Last one, enable ioc and short packet detect if necessary
+ ptd->link = UHCI_PTR_TERM;
+ ptd->status |= TD_CTRL_IOC;
+ if( bytes_to_transfer < max_packet_size && ( pid == USB_PID_IN ) )
+ {
+ //ptd->status |= TD_CTRL_SPD;
+ }
+ }
+
+ pthis = pnext;
+ toggle ^= 1;
+ if( pthis )
+ ListNext( &td_list, pthis, pnext );
+
+ }
+
+ ListFirst( &td_list, pthis );
+ RemoveEntryList( &td_list );
+
+ lock_qh_pool( &uhci->qh_pool, TRUE );
+ pqh = alloc_qh( &uhci->qh_pool );
+ unlock_qh_pool( &uhci->qh_pool, TRUE);
+
+ if( pqh == NULL )
+ {
+ lock_td_pool( &uhci->td_pool, TRUE );
+
+ if( pthis )
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+
+ urb->td_count = td_count;
+
+ uhci_insert_tds_qh( pqh, ( ( PTD_EXTENSION )pthis )->ptd );
+ uhci_insert_qh_urb( urb, pqh );
+ urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ;
+ usb_endp_busy_count_inc( urb->pendp );
+ uhci_insert_urb_to_schedule( uhci, urb, ret );
+
+ if( ret == FALSE )
+ {
+ // undo all we have done
+ RemoveEntryList( &pqh->pqhe->vert_link ); //remove qh from td_chain
+ RemoveEntryList( &urb->trasac_list );
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ if( pthis )
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ lock_qh_pool( &uhci->qh_pool, TRUE );
+ if( pqh )
+ free_qh( &uhci->qh_pool, pqh );
+ unlock_qh_pool( &uhci->qh_pool, TRUE );
+
+ InitializeListHead( &urb->trasac_list );
+ usb_endp_busy_count_dec( urb->pendp );
+ urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( old_toggle ? USB_ENDP_FLAG_DATATOGGLE : 0 ) ;
+ return STATUS_UNSUCCESSFUL;
+ }
+ return STATUS_SUCCESS;
+
+}
+
+static NTSTATUS
+uhci_internal_submit_ctrl(
+PUHCI_DEV uhci,
+PURB urb
+)
+{
+
+ LIST_ENTRY td_list, *pthis, *pnext;
+ LONG i, td_count;
+ LONG toggle;
+ LONG max_packet_size, bytes_to_transfer, bytes_rest, start_idx;
+ PUHCI_TD ptd;
+ PUHCI_QH pqh;
+ ULONG dev_addr;
+ PUSB_DEV pdev;
+ BOOL ret;
+
+ if( uhci == NULL || urb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ toggle = 0;
+ bytes_rest = urb->rest_bytes;
+ bytes_to_transfer = urb->bytes_to_transfer;
+ max_packet_size = endp_max_packet_size( urb->pendp );
+ start_idx = urb->data_length - urb->rest_bytes;
+ td_count = 2 + ( urb->bytes_to_transfer + max_packet_size - 1 ) / max_packet_size;
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+
+ if( can_transfer( &uhci->td_pool, td_count ) == FALSE )
+ {
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ ptd = alloc_tds( &uhci->td_pool, td_count );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ if( ptd == NULL )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ InsertTailList( &ptd->ptde->vert_link, &td_list );
+
+ ListFirst( &td_list, pthis );
+ ListNext( &td_list, pthis, pnext );
+
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+
+ pdev = dev_from_endp( urb->pendp );
+ dev_addr = pdev->dev_addr;
+
+ if( dev_state( pdev ) <= USB_DEV_STATE_RESET )
+ dev_addr = 0;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_internal_submit_ctrl(): dev_addr =0x%x\n", \
+ dev_addr ) );
+
+ RtlCopyMemory( uhci->io_buf, urb->setup_packet, 8 );
+
+ if( ( urb->setup_packet[ 0 ] & USB_DIR_IN ) == 0 ) //out
+ RtlCopyMemory( &uhci->io_buf[ 8 ], urb->data_buffer, bytes_to_transfer );
+ else
+ RtlZeroMemory( &uhci->io_buf[ 8 ], bytes_to_transfer );
+
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT ) | ( TD_CTRL_ACTIVE ),
+ ( 7 << 21 )
+ | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 )
+ | ( dev_addr << 8 )
+ | ( USB_PID_SETUP ),
+ //uhci->io_buf_logic_addr.LowPart );
+ MmGetPhysicalAddress( urb->setup_packet ).LowPart );
+
+ ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr;
+ pthis = pnext;
+ ListNext( &td_list, pthis, pnext );
+
+ urb->pipe = ( ( max_packet_size - 1 ) << 21 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( dev_addr << 8 )
+ | ( pdev->flags & USB_DEV_FLAG_LOW_SPEED )
+ | USB_ENDPOINT_XFER_CONTROL;
+
+ for( i = 0, toggle = 1; ( ( i < td_count - 2 ) && pthis ); i++, toggle ^= 1 )
+ {
+ //construct tds for DATA packets of data stage.
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT )
+ | ( TD_CTRL_ACTIVE ),
+ ( ( bytes_to_transfer > max_packet_size ? max_packet_size - 1 : bytes_to_transfer - 1 ) << 21 )
+ | ( toggle << 19 )
+ | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 )
+ | ( dev_addr << 8 )
+ | ( ( urb->setup_packet[ 0 ] & USB_DIR_IN ) ? USB_PID_IN : USB_PID_OUT ),
+ //uhci->io_buf_logic_addr.LowPart + 8 + i * max_packet_size );
+ MmGetPhysicalAddress( &urb->data_buffer[ start_idx + i * max_packet_size ] ).LowPart );
+
+ if( pnext )
+ ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr;
+
+ if( i < td_count - 3 )
+ {
+ bytes_to_transfer -= max_packet_size;
+ }
+ else
+ {
+ if( bytes_to_transfer > 0 )
+ {
+ if( bytes_to_transfer < max_packet_size && ( urb->setup_packet[ 0 ] & USB_DIR_IN ) )
+ ptd->status |= TD_CTRL_SPD;
+ }
+ }
+ pthis = pnext;
+
+ if( pthis )
+ ListNext( &td_list, pthis, pnext );
+ }
+
+ if( pnext )
+ ptd->link = ( ( PTD_EXTENSION ) pnext )->ptd->phy_addr;
+
+ ListFirstPrev( &td_list, pthis );
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+
+ //the last is an IN transaction
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT )
+ | ( TD_CTRL_ACTIVE | TD_CTRL_IOC ),
+ ( UHCI_NULL_DATA_SIZE << 21 )
+ | ( 1 << 19 )
+ | ( ( ( ULONG )endp_num( urb->pendp ) ) << 15 )
+ | ( dev_addr << 8 )
+ | ( ( td_count > 2 )
+ ? ( ( urb->setup_packet[ 0 ] & USB_DIR_IN )
+ ? USB_PID_OUT : USB_PID_IN )
+ : USB_PID_IN ),
+ 0 );
+
+ ptd->link = UHCI_PTR_TERM;
+
+ ListFirst( &td_list, pthis );
+ RemoveEntryList( &td_list );
+
+ lock_qh_pool( &uhci->qh_pool, TRUE );
+ pqh = alloc_qh( &uhci->qh_pool );
+ unlock_qh_pool( &uhci->qh_pool, TRUE);
+
+ if( pqh == NULL )
+ {
+ lock_td_pool( &uhci->td_pool, TRUE );
+ if( pthis )
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+
+ urb->td_count = td_count;
+
+ uhci_insert_tds_qh( pqh, ( ( PTD_EXTENSION )pthis )->ptd );
+ uhci_insert_qh_urb( urb, pqh );
+
+ usb_endp_busy_count_inc( urb->pendp );
+ uhci_insert_urb_to_schedule( uhci, urb, ret );
+ if( ret == FALSE )
+ {
+ RemoveEntryList( &pqh->pqhe->vert_link );
+ RemoveEntryList( &urb->trasac_list );
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ if( pthis )
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ lock_qh_pool( &uhci->qh_pool, TRUE );
+ if( pqh )
+ free_qh( &uhci->qh_pool, pqh );
+ unlock_qh_pool( &uhci->qh_pool, TRUE );
+
+ InitializeListHead( &urb->trasac_list );
+ usb_endp_busy_count_dec( urb->pendp );
+ return STATUS_UNSUCCESSFUL;
+ }
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+uhci_internal_submit_int(
+PUHCI_DEV uhci,
+PURB urb
+)
+{
+ LIST_ENTRY td_list, *pthis, *pnext;
+ LONG i;
+ LONG toggle = 0;
+ LONG max_packet_size;
+ PUHCI_TD ptd;
+ BOOL ret;
+
+ if( uhci == NULL || urb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ toggle = ( urb->pendp->flags & USB_ENDP_FLAG_DATATOGGLE ) ? TRUE : FALSE;
+ max_packet_size = endp_max_packet_size( urb->pendp );
+
+ if( max_packet_size < urb->data_length || max_packet_size == 0 || max_packet_size > 64 )
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ ptd = alloc_td( &uhci->td_pool );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ if( ptd == NULL )
+ return STATUS_NO_MORE_ENTRIES;
+
+ for( i = 1; i <= 7; i++ )
+ {
+ if( ( ( ULONG )max_packet_size ) >> i )
+ continue;
+ else
+ break;
+ }
+
+ i --;
+ i &= 7;
+
+ urb->pipe = ( ( ( ULONG )urb->pendp->pusb_endp_desc->bInterval ) << 24 )
+ | ( i << 21 )
+ | ( toggle << 19 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 )
+ | USB_DIR_IN
+ | ( dev_from_endp( urb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED )
+ | USB_ENDPOINT_XFER_INT;
+
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT )
+ | ( TD_CTRL_ACTIVE )
+ | ( ( urb->data_length < max_packet_size ? TD_CTRL_SPD : 0 ) )
+ | ( TD_CTRL_IOC ),
+ ( ( ( ULONG )max_packet_size - 1 ) << 21 )
+ | ( toggle << 19 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr & 0x7f) << 8 )
+ | USB_PID_IN,
+ MmGetPhysicalAddress( urb->data_buffer ).LowPart );
+
+ toggle ^= 1;
+ urb->td_count = 1;
+
+ InitializeListHead( &urb->trasac_list );
+ InsertTailList( &urb->trasac_list, &ptd->ptde->vert_link );
+
+ //indirectly guarded by pending_endp_list_lock
+ if( uhci_claim_bandwidth( uhci, urb, TRUE ) == FALSE )
+ {
+ InitializeListHead( &urb->trasac_list );
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ free_td( &uhci->td_pool, ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+
+ urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE )
+ | ( toggle << 31 );
+ usb_endp_busy_count_inc( urb->pendp );
+
+ uhci_insert_urb_to_schedule( uhci, urb, ret );
+
+ if( ret == FALSE )
+ {
+ lock_td_pool( &uhci->td_pool, TRUE );
+ if( ptd )
+ free_td( &uhci->td_pool, ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ InitializeListHead( &urb->trasac_list );
+ usb_endp_busy_count_dec( urb->pendp );
+ urb->pendp->flags = ( urb->pendp->flags & ~USB_ENDP_FLAG_DATATOGGLE ) | ( ( toggle ^ 1 ) << 31 );
+ uhci_claim_bandwidth( uhci, urb, FALSE );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+uhci_internal_submit_iso(
+PUHCI_DEV uhci,
+PURB urb
+)
+{
+ PUHCI_TD ptd;
+ LIST_ENTRY td_list, *pthis, *pnext;
+ int i;
+ BOOL toggle, ret;
+ KIRQL old_irql;
+
+ if( uhci == NULL || urb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( urb->iso_frame_count == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+
+ if( can_transfer( &uhci->td_pool, urb->iso_frame_count ) == FALSE )
+ {
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+ }
+
+ ptd = alloc_tds( &uhci->td_pool, urb->iso_frame_count );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+
+ if( ptd == NULL )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ InsertTailList( &ptd->ptde->vert_link, &td_list );
+ ListFirst( &td_list, pthis );
+
+ urb->td_count = urb->iso_frame_count;
+
+ urb->pipe = ( ( ( ULONG )urb->iso_packet_desc[ 0 ].length ) << 21 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 )
+ | ( ( ULONG )endp_dir( urb->pendp ) )
+ | USB_ENDPOINT_XFER_ISOC;
+
+
+ for( i = 0; i < urb->iso_frame_count && pthis; i++ )
+ {
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+ uhci_fill_td( ptd,
+ ( 3 << TD_CTRL_C_ERR_SHIFT )
+ | ( TD_CTRL_ACTIVE )
+ | ( TD_CTRL_IOS ),
+ ( ( ( ULONG )urb->iso_packet_desc[ i ].length - 1 ) << 21 )
+ | ( 0 << 19 )
+ | ( ( ULONG )endp_num( urb->pendp ) << 15 )
+ | ( ( ( ULONG )dev_from_endp( urb->pendp )->dev_addr ) << 8 )
+ | ( ( urb->pendp->pusb_endp_desc->bEndpointAddress & USB_DIR_IN )
+ ? USB_PID_OUT : USB_PID_IN ),
+ MmGetPhysicalAddress( &urb->data_buffer[ urb->iso_packet_desc[ i ].offset ] ).LowPart );
+
+ toggle ^= 1;
+ ListNext( &td_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ ptd->status |= TD_CTRL_IOC; //need interrupt
+
+ ListFirst( &td_list, pthis );
+ RemoveEntryList( &td_list );
+
+ InsertTailList( pthis, &urb->trasac_list );
+
+ //indirectly guarded by pending_endp_list_lock
+ if( uhci_claim_bandwidth( uhci, urb, TRUE ) == FALSE )
+ {
+ //bad news: we can not allocate the enough bandwidth for the urb
+ RemoveEntryList( &urb->trasac_list );
+ InitializeListHead( &urb->trasac_list );
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ return STATUS_NO_MORE_ENTRIES;
+
+ }
+ usb_endp_busy_count_inc( urb->pendp );
+ uhci_insert_urb_to_schedule( uhci, urb, ret );
+ if( ret == FALSE )
+ {
+ usb_endp_busy_count_dec( urb->pendp );
+ RemoveEntryList( &urb->trasac_list );
+
+ lock_td_pool( &uhci->td_pool, TRUE );
+ free_tds( &uhci->td_pool, ( ( PTD_EXTENSION )pthis )->ptd );
+ unlock_td_pool( &uhci->td_pool, TRUE );
+ uhci_claim_bandwidth( uhci, urb, FALSE );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static BOOL
+uhci_is_xfer_finished(
+PURB urb
+)
+// runs in uhci_isr
+{
+ PLIST_ENTRY pthis, pnext;
+ PUHCI_TD ptd;
+ PUHCI_QH pqh;
+ BOOL ret;
+ PTD_EXTENSION ptde;
+
+ if( urb->last_finished_td == NULL )
+ {
+ urb->last_finished_td = &urb->trasac_list;
+ }
+
+ if( &urb->trasac_list == urb->last_finished_td )
+ ListFirst( &urb->trasac_list, pthis )
+ else
+ ListNext( &urb->trasac_list, urb->last_finished_td, pthis );
+
+ while( pthis )
+ {
+ if( ( ( ( PTD_EXTENSION )pthis )->flags & UHCI_ITEM_FLAG_TYPE )
+ != UHCI_ITEM_FLAG_TD )
+ {
+ ListNext( &urb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ continue;
+ }
+ else
+ {
+ ptde = ( PTD_EXTENSION )pthis;
+ ptd = ptde->ptd;
+ ASSERT( ptd != NULL );
+
+ if( ptd->status & TD_CTRL_ACTIVE )
+ {
+ //still active
+ ret = FALSE;
+ break;
+ }
+ //let's see whether error occured
+ if( ( ptd->status & TD_CTRL_ANY_ERROR ) == 0 )
+ {
+ urb->last_finished_td = pthis;
+ ListNext( &urb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ continue;
+ }
+ else
+ {
+ urb->status = ptd->status;
+ pthis = NULL;
+ continue;
+ }
+ }
+
+ }
+
+ if( pthis == NULL )
+ ret = TRUE;
+
+ return ret;
+}
+
+static BOOL
+uhci_remove_urb_from_schedule(
+PUHCI_DEV uhci,
+PURB urb
+)
+// executed in isr, and have frame_list_lock acquired, so
+// never try to acquire any spin-lock
+// remove the bulk urb from schedule, and mark it not in
+// the schedule
+{
+ BOOL ret = FALSE;
+ {
+ switch( urb->pipe & USB_ENDPOINT_XFERTYPE_MASK )
+ {
+ case USB_ENDPOINT_XFER_BULK:
+ {
+ ret = uhci_remove_bulk_from_schedule( uhci, urb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ ret = uhci_remove_ctrl_from_schedule( uhci, urb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ ret = uhci_remove_int_from_schedule( uhci, urb );
+ break;
+ }
+ case USB_ENDPOINT_XFER_ISOC:
+ {
+ ret = uhci_remove_iso_from_schedule( uhci, urb );
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static BOOL
+uhci_remove_bulk_from_schedule(
+PUHCI_DEV uhci,
+PURB urb
+)
+// executed in isr, and have frame_list_lock acquired, so
+// never try to acquire any spin-lock
+// remove the bulk urb from schedule, and mark it not in
+// the schedule
+{
+
+ PUHCI_QH pqh, pnext_qh, pprev_qh;
+ PLIST_ENTRY pthis, pnext, pprev;
+ LONG i;
+
+ if( uhci == NULL || urb == NULL )
+ return FALSE;
+
+ ListFirst( &urb->trasac_list, pthis );
+ pqh = ( ( PQH_EXTENSION )pthis )->pqh;
+
+ ListFirst( &pqh->pqhe->hori_link, pnext );
+ ListFirstPrev( &pqh->pqhe->hori_link, pprev );
+
+ if( pprev == NULL || pnext == NULL )
+ return FALSE;
+
+ pnext_qh = struct_ptr( pnext, QH_EXTENSION, hori_link )->pqh;
+ pprev_qh = struct_ptr( pprev, QH_EXTENSION, hori_link )->pqh;
+
+ if( pprev != pnext )
+ {
+ //not the last one
+ pprev_qh->link = pnext_qh->phy_addr;
+ }
+ else
+ {
+ //only two qhs in the list
+ for( i = 0; i < UHCI_MAX_SKELQHS; i++ )
+ {
+ if( pprev_qh == uhci->skel_qh[ i ] )
+ {
+ break;
+ }
+ }
+ ASSERT( i < UHCI_MAX_SKELQHS - 1 );
+ pprev_qh->link = uhci->skel_qh[ i + 1 ]->phy_addr;
+ }
+ RemoveEntryList( &pqh->pqhe->hori_link );
+
+ urb->flags &= ~URB_FLAG_IN_SCHEDULE;
+
+ if( ( urb->pipe & USB_DEV_FLAG_LOW_SPEED ) == 0 )
+ uhci_drop_fsbr( uhci );
+
+ return TRUE;
+}
+
+static BOOL
+uhci_remove_iso_from_schedule(
+PUHCI_DEV uhci,
+PURB urb
+)
+{
+ PUHCI_TD ptd, pnext_td, pprev_td;
+ PLIST_ENTRY pthis, pnext, pprev;
+ int i, idx;
+
+ if( uhci == NULL || urb == NULL )
+ return FALSE;
+
+ ListFirst( &urb->trasac_list, pthis );
+
+ for( i = 0; i < urb->iso_frame_count && pthis; i++ )
+ {
+ ptd = ( ( PTD_EXTENSION )pthis )->ptd;
+ idx = ( urb->iso_start_frame + i ) & ( UHCI_MAX_FRAMES - 1 );
+
+ ListFirstPrev( &ptd->ptde->hori_link, pprev );
+
+ if( pprev == NULL )
+ return FALSE;
+
+ if( pprev == &uhci->frame_list_cpu[ idx ].td_link )
+ {
+ uhci->frame_list[ idx ] = ptd->link;
+ }
+ else
+ {
+ pprev_td = struct_ptr( pprev, TD_EXTENSION, hori_link )->ptd;
+ pprev_td->link = ptd->link;
+ }
+
+ RemoveEntryList( &ptd->ptde->hori_link );
+ ListNext( &urb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+
+ urb->flags &= ~URB_FLAG_IN_SCHEDULE;
+ return TRUE;
+}
+
+static BOOL
+uhci_remove_int_from_schedule(
+PUHCI_DEV uhci,
+PURB urb
+)
+{
+ PUHCI_TD ptd, pnext_td, pprev_td;
+ PLIST_ENTRY pthis, pnext, pprev;
+ LONG i;
+
+ if( uhci == NULL || urb == NULL )
+ return FALSE;
+
+ ListFirst( &urb->trasac_list, pthis );
+ ptd = ( ( PTD_EXTENSION )pthis )->ptd;
+ ListFirst( &ptd->ptde->hori_link, pnext );
+ ListFirstPrev( &ptd->ptde->hori_link, pprev );
+
+ if( pprev == NULL || pnext == NULL )
+ return FALSE;
+
+ pnext_td = struct_ptr( pnext, TD_EXTENSION, hori_link )->ptd;
+ pprev_td = struct_ptr( pprev, TD_EXTENSION, hori_link )->ptd;
+
+ if( pprev_td != pnext_td )
+ pprev_td->link = pnext_td->phy_addr;
+ else
+ {
+ //the last one
+ for( i = UHCI_MAX_SKELTDS - 2; i >= 0; i-- )
+ {
+ //UHCI_MAX_SKELTDS -1 skel tds for int transfer
+ if( pprev_td == uhci->skel_td[ i ] )
+ break;
+ }
+
+ ASSERT( i >= 0 );
+ if( i == 0 )
+ {
+ pprev_td->link = uhci->skel_qh[ 0 ]->phy_addr;
+ }
+ else
+ {
+ pprev_td->link = uhci->skel_td[ i - 1 ]->phy_addr;
+ }
+ }
+ RemoveEntryList( &ptd->ptde->hori_link );
+
+ urb->flags &= ~URB_FLAG_IN_SCHEDULE;
+ return TRUE;
+}
+
+static BOOL
+uhci_insert_tds_qh(
+PUHCI_QH pqh,
+PUHCI_TD td_chain
+)
+{
+ if( pqh == NULL || td_chain == NULL )
+ return FALSE;
+
+ InsertTailList( &td_chain->ptde->vert_link, &pqh->pqhe->vert_link );
+ pqh->element = td_chain->phy_addr;
+ return TRUE;
+}
+
+static BOOL
+uhci_insert_qh_urb(
+PURB urb,
+PUHCI_QH qh_chain
+)
+{
+ if( urb == NULL || qh_chain == NULL )
+ return FALSE;
+
+ InsertTailList( &qh_chain->pqhe->vert_link, &urb->trasac_list );
+ qh_chain->pqhe->purb = urb;
+ return TRUE;
+}
+
+static BOOL
+uhci_insert_urb_schedule(
+PUHCI_DEV uhci,
+PURB urb
+)
+// must have dev_lock and frame_list_lock acquired
+{
+ PUHCI_QH pqh, pskel_qh, pnext_qh;
+ PUHCI_TD ptd, plast_td;
+ PLIST_ENTRY pthis, pnext;
+ int i;
+
+ if( uhci == NULL || urb == NULL )
+ return FALSE;
+
+ ListFirst( &urb->trasac_list, pthis );
+ if( pthis == NULL )
+ return FALSE;
+
+ InsertTailList( &uhci->urb_list, ( PLIST_ENTRY )urb );
+
+ urb->flags &= ~URB_FLAG_STATE_MASK;
+ urb->flags |= URB_FLAG_STATE_IN_PROCESS | URB_FLAG_IN_SCHEDULE;
+
+
+ switch( endp_type( urb->pendp ) )
+ {
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ pqh = ( ( PQH_EXTENSION )pthis )->pqh;
+
+ if( ( dev_from_endp( urb->pendp )->flags & USB_DEV_FLAG_LOW_SPEED ) == 0 )
+ {
+ pskel_qh = uhci->skel_hs_control_qh;
+ pnext_qh = uhci->skel_bulk_qh;
+ }
+ else
+ {
+ pskel_qh = uhci->skel_ls_control_qh;
+ pnext_qh = uhci->skel_hs_control_qh;
+ }
+
+ ListFirstPrev( &pskel_qh->pqhe->hori_link,
+ pthis );
+
+ if( pthis == NULL )
+ pthis = &pskel_qh->pqhe->hori_link;
+
+ InsertTailList( &pskel_qh->pqhe->hori_link, &pqh->pqhe->hori_link );
+ pqh->link = pnext_qh->phy_addr;
+ struct_ptr( pthis, QH_EXTENSION, hori_link )->pqh->link = pqh->phy_addr;
+
+ //full speed band reclaimation
+ if( ( urb->pipe & USB_DEV_FLAG_LOW_SPEED ) == 0 )
+ {
+ uhci->fsbr_cnt++;
+ if( uhci->fsbr_cnt == 1 )
+ {
+ uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr;
+ }
+ }
+ return TRUE;
+ }
+ case USB_ENDPOINT_XFER_BULK:
+ {
+ pqh = ( ( PQH_EXTENSION )pthis )->pqh;
+
+ ListFirstPrev( &uhci->skel_bulk_qh->pqhe->hori_link,
+ pthis );
+
+ if( pthis == NULL )
+ pthis = &uhci->skel_bulk_qh->pqhe->hori_link;
+
+ InsertTailList( &uhci->skel_bulk_qh->pqhe->hori_link,
+ &pqh->pqhe->hori_link );
+
+ pqh->link = uhci->skel_term_qh->phy_addr;
+ struct_ptr( pthis, QH_EXTENSION, hori_link )->pqh->link =
+ pqh->phy_addr;
+
+ //full speed band reclaimation
+ uhci->fsbr_cnt++;
+ if( uhci->fsbr_cnt == 1 )
+ {
+ uhci->skel_term_qh->link = uhci->skel_hs_control_qh->phy_addr;;
+ }
+
+ return TRUE;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ //bandwidth claim is done outside
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+
+ get_int_idx( urb, i );
+
+ ListFirstPrev( &uhci->skel_td[ i ]->ptde->hori_link, pthis );
+ if( pthis == NULL )
+ pthis = &uhci->skel_td[ i ]->ptde->hori_link;
+
+ InsertTailList( &uhci->skel_td[ i ]->ptde->hori_link, &ptd->ptde->hori_link );
+
+ if( i > 0 )
+ {
+ ptd->link = uhci->skel_td[ i - 1 ]->phy_addr;
+ }
+ else if( i == 0 )
+ {
+ ptd->link = uhci->skel_qh[ 0 ]->phy_addr;
+ }
+ //finally link the previous td to this td
+ struct_ptr( pthis, TD_EXTENSION, hori_link )->ptd->link = ptd->phy_addr;
+ return TRUE;
+ }
+ case USB_ENDPOINT_XFER_ISOC:
+ {
+
+ for( i = 0; i < urb->iso_frame_count; i++ )
+ {
+ ptd = ( ( PTD_EXTENSION ) pthis )->ptd;
+ InsertTailList( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link,
+ &ptd->ptde->hori_link );
+
+ if( IsListEmpty( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link ) == TRUE )
+ {
+ ptd->link = uhci->frame_list[ ( urb->iso_start_frame + i ) & 0x3ff ];
+ uhci->frame_list[ i ] = ptd->phy_addr;
+ }
+ else
+ {
+ ListFirstPrev( &uhci->frame_list_cpu[ ( urb->iso_start_frame + i ) & 0x3ff ].td_link, pnext );
+ plast_td = struct_ptr( pnext, TD_EXTENSION, hori_link )->ptd;
+ ptd->link = plast_td->link;
+ plast_td->link = ptd->phy_addr;
+ }
+
+ ListNext( &urb->trasac_list, pthis, pnext );
+ pthis = pnext;
+ }
+ return TRUE;
+
+ }
+ }
+ return FALSE;
+}
+
+BOOLEAN
+uhci_sync_insert_urb_schedule(
+PVOID context
+)
+//this function used as the KeSynchronizeExecution param to delegate control to uhci_insert_urb_schedule
+{
+ PSYNC_PARAM sync_param;
+ PUHCI_DEV uhci;
+ PURB purb;
+
+ sync_param = ( PSYNC_PARAM ) context;
+ if( sync_param == NULL )
+ return FALSE;
+
+ uhci = sync_param->uhci;
+ purb = ( PURB )sync_param->context;
+
+ if( uhci == NULL || purb == NULL )
+ return ( UCHAR )sync_param->ret = FALSE;
+
+ return ( UCHAR )( sync_param->ret = uhci_insert_urb_schedule( uhci, purb ) );
+}
+
+static BOOL
+uhci_claim_bandwidth(
+PUHCI_DEV uhci,
+PURB urb,
+BOOL claim_bw //true to claim band-width, false to free band-width
+)
+// be sure pending_endp_list_lock acuqired
+{
+
+ UCHAR type;
+ BOOL ls, can_alloc;
+ LONG bus_time, us;
+ LONG i, idx, j, start_frame, interval;
+
+ if( urb == NULL )
+ return FALSE;
+
+ can_alloc = TRUE;
+
+ type = ( UCHAR )( urb->pipe & USB_ENDPOINT_XFERTYPE_MASK );
+ if( type == USB_ENDPOINT_XFER_BULK || type == USB_ENDPOINT_XFER_CONTROL )
+ {
+ return FALSE;
+ }
+
+ ls = ( urb->pipe & USB_DEV_FLAG_LOW_SPEED )
+ ? TRUE : FALSE;
+
+ if( type == USB_ENDPOINT_XFER_INT )
+ {
+ start_frame = 0;
+ i = urb->data_length;
+ bus_time = usb_calc_bus_time( ls, FALSE, FALSE, i );
+ us = ns_to_us( bus_time );
+
+ i = ( urb->pipe >> 24 ); //polling interval
+
+ for( interval = 0, j = 0; j < 8; j++ )
+ {
+ if( i & ( 1 << j ) )
+ {
+ interval = j;
+ }
+ }
+
+ interval = 1 << interval;
+ start_frame = interval - 1;
+
+ if( claim_bw )
+ {
+
+ for( idx = 0; idx < UHCI_MAX_FRAMES; idx += interval )
+ {
+ if( uhci->frame_bw[ idx ] < us )
+ {
+ can_alloc = FALSE;
+ break;
+ }
+ }
+
+ if( !can_alloc )
+ {
+ return FALSE;
+ }
+
+ for( idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval )
+ {
+ uhci->frame_bw[ idx ] -= us;
+ }
+ }
+ else
+ {
+ for( idx = start_frame; idx < UHCI_MAX_FRAMES; idx += interval )
+ {
+ uhci->frame_bw[ idx ] += us;
+ }
+ }
+
+ }
+ else if( type == USB_ENDPOINT_XFER_ISOC )
+ {
+ if( claim_bw )
+ {
+ for( i = 0; i < urb->iso_frame_count; i++ )
+ {
+ bus_time = usb_calc_bus_time(
+ FALSE,
+ ( urb->pipe & USB_DIR_IN )
+ ? TRUE : FALSE,
+ TRUE,
+ urb->iso_packet_desc[ i ].length );
+
+ urb->iso_packet_desc[ i ].bus_time = ns_to_us( bus_time );
+ }
+
+ for( i = 0; i < urb->iso_frame_count ; i++ )
+ {
+ if( uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ]
+ < urb->iso_packet_desc[ i ].bus_time )
+ {
+ can_alloc = FALSE;
+ break;
+ }
+ }
+
+ if( !can_alloc )
+ {
+ return FALSE;
+ }
+
+ for( i = 0; i < urb->iso_frame_count ; i++ )
+ {
+ uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ]
+ -= urb->iso_packet_desc[ i ].bus_time;
+ }
+ }
+ else
+ {
+ for( i = 0; i < urb->iso_frame_count ; i++ )
+ {
+ uhci->frame_bw[ ( urb->iso_start_frame + i ) & 0x3ff ]
+ += urb->iso_packet_desc[ i ].bus_time;
+ }
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+uhci_sync_cancel_urb(
+PVOID context
+)
+{
+ //cancel a single urb
+ PUHCI_DEV uhci;
+ PSYNC_PARAM sync_param;
+ PURB purb2, dest_urb;
+ PLIST_ENTRY pthis, pnext;
+ BOOL found = FALSE;
+
+ if( context == NULL )
+ return FALSE;
+
+ sync_param = ( PSYNC_PARAM )context;
+ uhci = sync_param->uhci;
+ dest_urb = ( PURB )sync_param->context;
+
+ if( uhci == NULL || dest_urb == NULL )
+ return ( UCHAR )sync_param->ret = FALSE;
+
+ ListFirst( &uhci->urb_list, pthis );
+ while( pthis )
+ {
+ purb2 = ( PURB ) pthis;
+ if( purb2 == dest_urb )
+ {
+ found = TRUE;
+ purb2->flags |= URB_FLAG_FORCE_CANCEL;
+ break;
+ }
+ ListNext( &uhci->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+ if( found )
+ uhci->skel_term_td->status |= TD_CTRL_IOC;
+
+ return ( UCHAR )( sync_param->ret = found );
+
+}
+
+NTSTATUS
+uhci_cancel_urb(
+PUHCI_DEV uhci,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+//note any fields of the purb can not be referenced unless it is found in some queue
+{
+
+ NTSTATUS status;
+ PLIST_ENTRY pthis, pnext;
+ BOOL found;
+ PURB purb2;
+
+ SYNC_PARAM sync_param;
+
+ USE_IRQL;
+
+ if( uhci == NULL || purb == NULL || pdev == NULL || pendp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( pdev, FALSE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ //delegate to remove device for this job
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ if( dev_from_endp( pendp ) != pdev )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if( endp_state( pendp ) == USB_ENDP_FLAG_STALL )
+ {
+ //it will be canceled in uhci_process_pending_endp
+ unlock_dev( pdev, FALSE );
+ return USB_STATUS_ENDPOINT_HALTED;
+ }
+
+ found = FALSE;
+ ListFirst( &pendp->urb_list, pthis );
+ while( pthis )
+ {
+ purb2 = ( PURB ) pthis;
+ if( purb2 == purb )
+ {
+ found = TRUE;
+ RemoveEntryList( pthis );
+ InitializeListHead( pthis );
+ break;
+ }
+ ListNext( &pendp->urb_list, pthis, pnext );
+ pthis = pnext;
+ }
+ unlock_dev( pdev, FALSE );
+
+ if( found )
+ {
+ purb->status = STATUS_CANCELLED;
+
+ uhci_generic_urb_completion( purb, purb->context );
+
+ lock_dev( pdev, FALSE );
+ pdev->ref_count --;
+ unlock_dev( pdev, FALSE );
+ return STATUS_SUCCESS;
+ }
+
+ // search the urb in the urb-list and try to cancel
+ sync_param.uhci = uhci;
+ sync_param.context = purb;
+
+ KeSynchronizeExecution( uhci->pdev_ext->uhci_int, uhci_sync_cancel_urb, &sync_param );
+
+ found = sync_param.ret;
+
+ if( found )
+ return USB_STATUS_CANCELING;
+
+ return STATUS_INVALID_PARAMETER;
+}
+
+VOID
+uhci_generic_urb_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ KIRQL cur_irql;
+ USE_IRQL;
+
+ old_irql = KeGetCurrentIrql();
+ if( old_irql > DISPATCH_LEVEL )
+ TRAP();
+
+ if( old_irql < DISPATCH_LEVEL )
+ KeRaiseIrql(DISPATCH_LEVEL, &old_irql);
+
+ pdev = purb->pdev;
+ if( purb == NULL )
+ return;
+
+ if( pdev == NULL )
+ return;
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ goto LBL_CLIENT_PROCESS;
+ }
+ if( usb_error( purb->status ) )
+ {
+ pdev->error_count++;
+ }
+
+ if( purb->pendp == &pdev->default_endp )
+ {
+ if( usb_halted( purb->status ) )
+ {
+ pdev->time_out_count++;
+ if( pdev->time_out_count > 3 )
+ {
+ dev_set_state( pdev, USB_DEV_STATE_ZOMB );
+ uhci_dbg_print( DBGLVL_MAXIMUM, ("uhci_generic_urb_completion(): contiguous error 3 times, dev 0x%x is deactivated\n", pdev ) );
+ }
+ }
+ else
+ pdev->time_out_count = 0;
+
+ }
+ unlock_dev( pdev, TRUE );
+
+ LBL_CLIENT_PROCESS:
+ if( purb->completion )
+ purb->completion( purb, context );
+
+ if( old_irql < DISPATCH_LEVEL )
+ KeLowerIrql( old_irql );
+
+ return;
+}
+
+
+NTSTATUS
+uhci_rh_submit_urb(
+PUSB_DEV pdev,
+PURB purb
+)
+{
+ PUSB_DEV_MANAGER dev_mgr;
+ PTIMER_SVC ptimer;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUHCI_DEV uhci;
+ NTSTATUS status;
+#ifndef INCLUDE_EHCI
+ PHUB_EXTENSION hub_ext;
+#else
+ PHUB2_EXTENSION hub_ext;
+#endif
+ PUSB_PORT_STATUS ps, psret;
+ LONG i;
+
+ USE_IRQL;
+ if( pdev == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+
+ KeAcquireSpinLock( &dev_mgr->timer_svc_list_lock, &old_irql );
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ uhci = uhci_from_hcd( pdev->hcd );
+ psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet;
+
+#ifndef INCLUDE_EHCI
+ hub_ext = ( ( PHUB_EXTENSION )pdev->dev_ext );
+#else
+ hub_ext = ( ( PHUB2_EXTENSION )pdev->dev_ext );
+#endif
+
+ switch( endp_type( purb->pendp ) )
+ {
+ case USB_ENDPOINT_XFER_CONTROL:
+ {
+ if( psetup->bmRequestType == 0xa3 \
+ && psetup->bRequest == USB_REQ_GET_STATUS )
+ {
+ //get-port-status
+ if( psetup->wIndex == 0 \
+ || psetup->wIndex > 2 \
+ || psetup->wLength < 4 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if( psetup->wIndex == 1 )
+ {
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC1 ) );
+ ps = &hub_ext->rh_port1_status;
+ }
+ else
+ {
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC2 ) );
+ ps = &hub_ext->rh_port2_status;
+ }
+
+ psret = ( PUSB_PORT_STATUS )purb->data_buffer;
+ ps->wPortStatus = 0;
+
+ if( status & USBPORTSC_CCS )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_CONNECTION;
+ }
+ if( status & USBPORTSC_PE )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_ENABLE;
+ }
+ if( status & USBPORTSC_PR )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_RESET;
+ }
+ if( status & USBPORTSC_SUSP )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_SUSPEND;
+ }
+ if( status & USBPORTSC_LSDA )
+ {
+ ps->wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+ }
+
+ //always power on
+ ps->wPortStatus |= USB_PORT_STAT_POWER;
+
+ //now set change field
+ if( status & USBPORTSC_CSC )
+ {
+ ps->wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ }
+ if( status & USBPORTSC_PEC )
+ {
+ ps->wPortChange |= USB_PORT_STAT_C_ENABLE;
+ }
+
+ //don't touch other fields, will be filled by
+ //other function
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( \
+ "uhci_rh_submit_urb(): get port status, wPortStatus=0x%x, wPortChange=0x%x, address=0x%x\n", \
+ ps->wPortStatus,
+ ps->wPortChange,
+ ps ) );
+
+ psret->wPortChange = ps->wPortChange;
+ psret->wPortStatus = ps->wPortStatus;
+
+ purb->status = STATUS_SUCCESS;
+
+ break;
+ }
+ else if( psetup->bmRequestType == 0x23 \
+ && psetup->bRequest == USB_REQ_CLEAR_FEATURE )
+ {
+ //clear-port-feature
+ if( psetup->wIndex == 0 || psetup->wIndex > 2 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if( psetup->wIndex == 1 )
+ {
+ i = USBPORTSC1;
+ ps = &hub_ext->rh_port1_status;
+ }
+ else
+ {
+ i = USBPORTSC2;
+ ps = &hub_ext->rh_port2_status;
+ }
+
+ purb->status = STATUS_SUCCESS;
+ switch( psetup->wValue )
+ {
+ case USB_PORT_FEAT_C_CONNECTION:
+ {
+ ps->wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
+ SET_RH_PORTSTAT( i, USBPORTSC_CSC );
+ status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear csc, port%d=0x%x\n", psetup->wIndex, status ) );
+ break;
+ }
+ case USB_PORT_FEAT_C_ENABLE:
+ {
+ ps->wPortChange &= ~USB_PORT_STAT_C_ENABLE;
+ SET_RH_PORTSTAT( i, USBPORTSC_PEC );
+ status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pec, port%d=0x%x\n", psetup->wIndex, status ) );
+ break;
+ }
+ case USB_PORT_FEAT_C_RESET:
+ {
+ ps->wPortChange &= ~USB_PORT_STAT_C_RESET;
+ //the reset signal is down in rh_timer_svc_reset_port_completion
+ //so enable the port here
+ status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pr, enable pe, port%d=0x%x\n", psetup->wIndex, status ) );
+ break;
+ }
+ case USB_PORT_FEAT_ENABLE:
+ {
+ ps->wPortStatus &= ~USB_PORT_STAT_ENABLE;
+ CLR_RH_PORTSTAT( i, USBPORTSC_PE );
+ status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): clear pe, port%d=0x%x\n", psetup->wIndex, status ) );
+ break;
+ }
+ default:
+ purb->status = STATUS_UNSUCCESSFUL;
+ }
+ break;
+ }
+ else if( psetup->bmRequestType == 0xd3 \
+ && psetup->bRequest == HUB_REQ_GET_STATE )
+ {
+ // get bus state
+ if( psetup->wIndex == 0 ||\
+ psetup->wIndex > 2 ||\
+ psetup->wLength == 0 )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if( psetup->wIndex == 1 )
+ {
+ i = USBPORTSC1;
+ }
+ else
+ {
+ i = USBPORTSC2;
+ }
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + i ) );
+ purb->data_buffer[ 0 ] = ( status & USBPORTSC_LS );
+
+ // reverse the order
+ purb->data_buffer[ 0 ] ^= 0x3;
+ purb->status = STATUS_SUCCESS;
+ break;
+ }
+ else if( psetup->bmRequestType == 0x23 \
+ && psetup->bRequest == USB_REQ_SET_FEATURE )
+ {
+ //reset port
+ if( psetup->wValue != USB_PORT_FEAT_RESET )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ uhci_dbg_print( DBGLVL_MAXIMUM, ("uhci_rh_submit_urb(): set feature with wValue=0x%x\n", psetup->wValue ) );
+ break;
+ }
+ if( psetup->wIndex == 1 )
+ {
+ i = USBPORTSC1;
+ }
+ else
+ {
+ i = USBPORTSC2;
+ }
+
+ ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ ptimer->threshold = 0; // within [ 50ms, 60ms ], one tick is 10 ms
+ ptimer->context = ( ULONG )purb;
+ ptimer->pdev = pdev;
+ ptimer->func = rh_timer_svc_reset_port_completion;
+
+ //start the timer
+ pdev->ref_count += 2; //one for timer and one for urb
+
+ status = READ_PORT_USHORT( ( PUSHORT ) ( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): reset port, port%d=0x%x\n", psetup->wIndex, status ) );
+ InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link );
+ purb->status = STATUS_PENDING;
+ }
+ else
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+ case USB_ENDPOINT_XFER_INT:
+ {
+ ptimer = alloc_timer_svc( &dev_mgr->timer_svc_pool, 1 );
+ ptimer->threshold = RH_INTERVAL;
+ ptimer->context = ( ULONG )purb;
+ ptimer->pdev = pdev;
+ ptimer->func = rh_timer_svc_int_completion;
+
+ //start the timer
+ InsertTailList(&dev_mgr->timer_svc_list, &ptimer->timer_svc_link );
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_submit_urb(): current rh's ref_count=0x%x\n", pdev->ref_count ) );
+ pdev->ref_count += 2; //one for timer and one for urb
+
+ purb->status = STATUS_PENDING;
+ break;
+ }
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_ISOC:
+ default:
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ unlock_dev( pdev, FALSE );
+ KeReleaseSpinLock( &dev_mgr->timer_svc_list_lock, old_irql );
+ return purb->status;
+}
+
+//must have rh dev_lock acquired
+BOOL
+uhci_rh_reset_port(
+PHCD hcd,
+UCHAR port_idx
+)
+{
+ LONG i;
+ PUHCI_DEV uhci;
+ ULONG status;
+
+ if( port_idx != 1 && port_idx != 2 )
+ return FALSE;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ if( port_idx == 1 )
+ {
+ i = USBPORTSC1;
+ }
+ else
+ {
+ i = USBPORTSC2;
+ }
+
+ uhci = uhci_from_hcd( hcd );
+ //assert the reset signal,(implicitly disable the port)
+ SET_RH_PORTSTAT( i, USBPORTSC_PR );
+ usb_wait_ms_dpc( 50 );
+ //clear the reset signal, delay port enable till clearing port feature
+ CLR_RH_PORTSTAT( i, USBPORTSC_PR );
+ usb_wait_us_dpc( 10 );
+ SET_RH_PORTSTAT( i, USBPORTSC_PE );
+ //recovery time 10ms
+ usb_wait_ms_dpc( 10 );
+ SET_RH_PORTSTAT( i, 0x0a );
+
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + i ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_reset_port(): status after written=0x%x\n", status ) );
+
+ return TRUE;
+}
+
+NTSTATUS
+uhci_dispatch_irp(
+IN PDEVICE_OBJECT DeviceObject,
+IN PIRP irp
+)
+{
+ PDEVICE_EXTENSION pdev_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUHCI_DEV uhci;
+
+ pdev_ext = DeviceObject->DeviceExtension;
+ uhci = pdev_ext->uhci;
+
+ dev_mgr = uhci->hcd_interf.hcd_get_dev_mgr( &uhci->hcd_interf );
+ return dev_mgr_dispatch( dev_mgr, irp );
+}
+
+VOID
+uhci_unload(
+IN PDRIVER_OBJECT DriverObject
+)
+{
+ PDEVICE_OBJECT pdev;
+ PDEVICE_EXTENSION pdev_ext;
+ PUSB_DEV_MANAGER dev_mgr;
+ LONG i;
+
+ pdev = DriverObject->DeviceObject;
+
+ if( pdev == NULL )
+ return;
+
+ pdev_ext = pdev->DeviceExtension;
+ if( pdev_ext == NULL )
+ return;
+
+ dev_mgr = &g_dev_mgr;
+ if( dev_mgr == NULL )
+ return;
+ //
+ // set the termination flag
+ //
+ dev_mgr->term_flag = TRUE;
+ //
+ // wake up the thread if it is
+ //
+ KeSetEvent( &dev_mgr->wake_up_event, 0, FALSE );
+ KeWaitForSingleObject(
+ dev_mgr->pthread,
+ Executive,
+ KernelMode,
+ TRUE,
+ NULL);
+ ObDereferenceObject( dev_mgr->pthread );
+ dev_mgr->pthread = NULL;
+ // for( i = 0; i < dev_mgr->hcd_count; i++ )
+ // dev_mgr->hcd_array[ i ]->hcd_release( dev_mgr->hcd_array[ i ]);
+ dev_mgr_release_hcd( dev_mgr );
+
+ return;
+}
+
+//the following are for hcd interface methods
+VOID
+uhci_set_dev_mgr(
+struct _HCD* hcd,
+PUSB_DEV_MANAGER dev_mgr )
+{
+ hcd->dev_mgr = dev_mgr;
+}
+
+PUSB_DEV_MANAGER
+uhci_get_dev_mgr(
+struct _HCD* hcd
+)
+{
+ return hcd->dev_mgr;
+}
+
+ULONG
+uhci_get_type(
+struct _HCD* hcd
+)
+{
+ return ( hcd->flags & HCD_TYPE_MASK );
+}
+
+VOID
+uhci_set_id(
+struct _HCD* hcd,
+UCHAR id
+)
+{
+ hcd->flags &= ~HCD_ID_MASK;
+ hcd->flags |= ( HCD_ID_MASK & id );
+}
+
+UCHAR
+uhci_get_id(
+struct _HCD* hcd
+)
+{
+ return ( UCHAR )( hcd->flags & HCD_ID_MASK );
+}
+
+
+UCHAR
+uhci_alloc_addr(
+struct _HCD* hcd
+)
+{
+ LONG i;
+ if( hcd == NULL )
+ return 0;
+
+ for( i = 1; i < MAX_DEVS; i ++ )
+ {
+ if( hcd->dev_addr_map[ i >> 3 ] & ( 1 << ( i & 7 ) ) )
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( i >= MAX_DEVS )
+ return 0xff;
+
+ hcd->dev_addr_map[ i >> 3 ] |= ( 1 << ( i & 7 ) );
+ hcd->conn_count++;
+ return ( BYTE )i;
+}
+VOID
+uhci_free_addr(
+struct _HCD* hcd,
+UCHAR addr
+)
+{
+ if( addr & 0x80 )
+ return;
+
+ if( hcd == NULL )
+ return;
+
+ hcd->dev_addr_map[ addr >> 3 ] &= ~( 1 << ( addr & 7 ) );
+ return;
+
+}
+
+NTSTATUS
+uhci_submit_urb2(
+struct _HCD* hcd,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb )
+{
+ return uhci_submit_urb( uhci_from_hcd( hcd ), pdev, pendp, purb );
+}
+
+PUSB_DEV
+uhci_get_root_hub(
+struct _HCD* hcd
+)
+{
+ return uhci_from_hcd( hcd )->root_hub;
+}
+VOID
+uhci_set_root_hub(
+struct _HCD* hcd,
+PUSB_DEV root_hub
+)
+{
+ if( hcd == NULL || root_hub == NULL )
+ return;
+ uhci_from_hcd( hcd )->root_hub = root_hub;
+ return;
+}
+
+BOOL
+uhci_remove_device2(
+struct _HCD* hcd,
+PUSB_DEV pdev
+)
+{
+ if( hcd == NULL || pdev == NULL )
+ return FALSE;
+
+ return uhci_remove_device( uhci_from_hcd( hcd ), pdev );
+}
+
+BOOL uhci_hcd_release(
+struct _HCD* hcd
+)
+{
+ PUHCI_DEV uhci;
+ PDEVICE_EXTENSION pdev_ext;
+
+ if( hcd == NULL )
+ return FALSE;
+
+
+ uhci = uhci_from_hcd( hcd );
+ pdev_ext = uhci->pdev_ext;
+
+ return uhci_release( pdev_ext->pdev_obj );
+}
+
+NTSTATUS
+uhci_cancel_urb2(
+struct _HCD* hcd,
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURB purb
+)
+{
+ PUHCI_DEV uhci;
+ if( hcd == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ uhci = uhci_from_hcd( hcd );
+ return uhci_cancel_urb( uhci, pdev, pendp, purb );
+}
+
+BOOL
+uhci_rh_get_dev_change(
+PHCD hcd,
+PBYTE buf
+)
+{
+ PUHCI_DEV uhci;
+ ULONG status;
+
+ if( hcd == NULL || buf == NULL )
+ return FALSE;
+
+ uhci = uhci_from_hcd( hcd );
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC1 ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "uhci_rh_get_dev_change(): rh port1 status=0x%x\n", status ) );
+
+ if( ( status & USBPORTSC_PEC ) || ( status & USBPORTSC_CSC ) )
+ {
+ buf[ 0 ] |= ( 1 << 1 );
+ }
+
+ status = READ_PORT_USHORT( ( PUSHORT )( uhci->port_base + USBPORTSC2 ) );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "rh_timer_svc_int_completion(): rh port2 status=0x%x\n", status ) );
+
+ if( ( status & USBPORTSC_PEC ) || ( status & USBPORTSC_CSC ) )
+ {
+ buf[ 0 ] |= ( 1 << 2 );
+ }
+ return TRUE;
+}
+
+NTSTATUS
+uhci_dispatch(
+PHCD hcd,
+LONG disp_code,
+PVOID param ) // locking depends on type of code
+{
+ ULONG status;
+
+ if( hcd == NULL )
+ return FALSE;
+
+ switch( disp_code )
+ {
+ case HCD_DISP_READ_PORT_COUNT:
+ {
+ if( param == NULL )
+ return STATUS_INVALID_PARAMETER;
+ *( ( PUCHAR )param ) = 2;
+ return STATUS_SUCCESS;
+ }
+ case HCD_DISP_READ_RH_DEV_CHANGE:
+ {
+ if( uhci_rh_get_dev_change( hcd, param ) == FALSE )
+ return STATUS_INVALID_PARAMETER;
+ return STATUS_SUCCESS;
+ }
+ }
+ return STATUS_NOT_IMPLEMENTED;
+
+}
+VOID
+uhci_init_hcd_interface(
+PUHCI_DEV uhci
+)
+{
+ uhci->hcd_interf.hcd_set_dev_mgr = uhci_set_dev_mgr;
+ uhci->hcd_interf.hcd_get_dev_mgr = uhci_get_dev_mgr;
+ uhci->hcd_interf.hcd_get_type = uhci_get_type;
+ uhci->hcd_interf.hcd_set_id = uhci_set_id;
+ uhci->hcd_interf.hcd_get_id = uhci_get_id;
+ uhci->hcd_interf.hcd_alloc_addr = uhci_alloc_addr;
+ uhci->hcd_interf.hcd_free_addr = uhci_free_addr;
+ uhci->hcd_interf.hcd_submit_urb = uhci_submit_urb2;
+ uhci->hcd_interf.hcd_generic_urb_completion = uhci_generic_urb_completion;
+ uhci->hcd_interf.hcd_get_root_hub = uhci_get_root_hub;
+ uhci->hcd_interf.hcd_set_root_hub = uhci_set_root_hub;
+ uhci->hcd_interf.hcd_remove_device = uhci_remove_device2;
+ uhci->hcd_interf.hcd_rh_reset_port = uhci_rh_reset_port;
+ uhci->hcd_interf.hcd_release = uhci_hcd_release;
+ uhci->hcd_interf.hcd_cancel_urb = uhci_cancel_urb2;
+ uhci->hcd_interf.hcd_start = uhci_start;
+ uhci->hcd_interf.hcd_dispatch = uhci_dispatch;
+
+ uhci->hcd_interf.flags = HCD_TYPE_UHCI; //hcd types | hcd id
+}
+
+
+NTSTATUS
+generic_dispatch_irp(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ PDEVEXT_HEADER dev_ext;
+
+ dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+
+ if( dev_ext && dev_ext->dispatch )
+ return dev_ext->dispatch( dev_obj, irp );
+
+ irp->IoStatus.Information = 0;
+
+ EXIT_DISPATCH( STATUS_UNSUCCESSFUL, irp );
+}
+
+
+VOID
+generic_start_io(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ PDEVEXT_HEADER dev_ext;
+
+ KIRQL old_irql;
+
+ IoAcquireCancelSpinLock( &old_irql );
+ if (irp != dev_obj->CurrentIrp || irp->Cancel)
+ {
+ IoReleaseCancelSpinLock(old_irql);
+ return;
+ }
+ else
+ {
+ IoSetCancelRoutine(irp, NULL);
+ IoReleaseCancelSpinLock(old_irql);
+ }
+
+ dev_ext = ( PDEVEXT_HEADER )dev_obj->DeviceExtension;
+
+ if( dev_ext && dev_ext->start_io )
+ {
+ dev_ext->start_io( dev_obj, irp );
+ return;
+ }
+
+ irp->IoStatus.Information = 0;
+ irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+
+ IoStartNextPacket( dev_obj, FALSE );
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+}
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ PDEVICE_OBJECT dev_obj = NULL;
+ BOOLEAN fRes;
+
+#if DBG
+ // should be done before any debug output is done.
+ // read our debug verbosity level from the registry
+ //NetacOD_GetRegistryDword( NetacOD_REGISTRY_PARAMETERS_PATH, //absolute registry path
+ // L"DebugLevel", // REG_DWORD ValueName
+ // &gDebugLevel ); // Value receiver
+
+ // debug_level = DBGLVL_MAXIMUM;
+#endif
+
+ uhci_dbg_print_cond( DBGLVL_MINIMUM , DEBUG_UHCI, ("Entering DriverEntry(), RegistryPath=\n %ws\n", RegistryPath->Buffer ));
+
+ // Remember our driver object, for when we create our child PDO
+ usb_driver_obj = DriverObject;
+
+ //
+ // Create dispatch points for create, close, unload
+ DriverObject->MajorFunction[ IRP_MJ_CREATE ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = generic_dispatch_irp;
+ DriverObject->DriverUnload = uhci_unload;
+
+ // User mode DeviceIoControl() calls will be routed here
+ DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = generic_dispatch_irp;
+
+ // User mode ReadFile()/WriteFile() calls will be routed here
+ DriverObject->MajorFunction[ IRP_MJ_WRITE ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_READ ] = generic_dispatch_irp;
+
+ DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_SCSI ] = generic_dispatch_irp;
+ DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = generic_dispatch_irp;
+
+ DriverObject->DriverStartIo = generic_start_io;
+ // routines for handling system PNP and power management requests
+ //DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = generic_dispatch_irp;
+
+ // The Functional Device Object (FDO) will not be created for PNP devices until
+ // this routine is called upon device plug-in.
+ RtlZeroMemory( &g_dev_mgr, sizeof( USB_DEV_MANAGER ) );
+ g_dev_mgr.usb_driver_obj = DriverObject;
+
+#ifdef INCLUDE_EHCI
+ ehci_probe( DriverObject, RegistryPath, &g_dev_mgr );
+#endif
+
+ uhci_probe( DriverObject, RegistryPath, &g_dev_mgr );
+
+ if( dev_mgr_strobe( &g_dev_mgr ) == FALSE )
+ {
+
+ dev_mgr_release_hcd( &g_dev_mgr );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ dev_mgr_start_hcd( &g_dev_mgr );
+
+ uhci_dbg_print_cond( DBGLVL_DEFAULT, DEBUG_UHCI, ("DriverEntry(): exiting... (%x)\n", ntStatus));
+ return STATUS_SUCCESS;
+}
+
+//note: the initialization will be in the following order
+// uhci_probe
+// dev_mgr_strobe
+// uhci_start
+
+// to kill dev_mgr_thread:
+// dev_mgr->term_flag = TRUE;
+// KeSetEvent( &dev_mgr->wake_up_event );
+// this piece of code must run at passive-level
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h b/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h
new file mode 100644
index 00000000000..4ea9e812556
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/uhciver.h
@@ -0,0 +1,13 @@
+#ifndef __UHCIVER_H__
+#define __UHCIVER_H__
+
+//#define _MULTI_UHCI
+//#define _TIANSHENG_DRIVER
+
+#ifdef _MULTI_UHCI
+#define UHCI_VER_STR "m564.a\0"
+#else
+#define UHCI_VER_STR "0564.d\0"
+#endif
+
+#endif
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/umss.c b/reactos/drivers/usb/nt4compat/usbdriver/umss.c
new file mode 100644
index 00000000000..76ee2799711
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/umss.c
@@ -0,0 +1,2443 @@
+/**
+ * umss.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "uhciver.h"
+#include "ntddk.h"
+#include "stdio.h"
+#include "debug.h"
+#include "umss.h"
+#include "usb.h"
+#include "hub.h"
+#include "storage\inc\srb.h"
+#include "storage\inc\ntddscsi.h"
+#include "storage\inc\scsi.h"
+
+#define UMSS_EXIT_DISPATCH( dev_OBJ, staTUS, iRp ) \
+{\
+ iRp->IoStatus.Status = staTUS;\
+ if( staTUS != STATUS_PENDING)\
+ {\
+ IoCompleteRequest( iRp, IO_NO_INCREMENT);\
+ return staTUS;\
+ }\
+ IoMarkIrpPending( iRp );\
+ IoStartPacket( dev_OBJ, iRp, NULL, NULL ); \
+ return STATUS_PENDING;\
+}
+
+#define UMSS_COMPLETE_START_IO( dev_OBJ, staTUS, iRP ) \
+{\
+ iRP->IoStatus.Status = staTUS;\
+ if( staTUS != STATUS_PENDING )\
+ {\
+ IoStartNextPacket( dev_OBJ, FALSE );\
+ IoCompleteRequest( iRP, IO_NO_INCREMENT );\
+ }\
+ return;\
+}
+
+extern VOID
+gendrv_startio(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+);
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwLoadDriver(
+IN PUNICODE_STRING DriverServiceName
+);
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ObOpenObjectByName(
+IN POBJECT_ATTRIBUTES ObjectAttributes,
+IN POBJECT_TYPE ObjectType OPTIONAL,
+IN KPROCESSOR_MODE AccessMode,
+IN OUT PACCESS_STATE AccessState OPTIONAL,
+IN ACCESS_MASK DesiredAccess OPTIONAL,
+IN OUT PVOID ParseContext OPTIONAL,
+OUT PHANDLE Handle
+);
+
+VOID
+umss_start_io(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+);
+
+NTSTATUS
+umss_port_dispatch_routine(
+PDEVICE_OBJECT pdev_obj,
+PIRP irp
+);
+
+BOOL
+umss_connect(
+PCONNECT_DATA dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+umss_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+BOOL
+umss_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+NTSTATUS
+umss_dispatch_routine(
+PDEVICE_OBJECT pdev_obj,
+PIRP irp
+);
+
+VOID
+umss_set_cfg_completion(
+PURB purb,
+PVOID pcontext
+);
+
+VOID
+umss_start_create_device(
+IN PVOID Parameter
+);
+
+BOOL
+umss_delete_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdrvr,
+PDEVICE_OBJECT dev_obj,
+BOOL is_if
+);
+
+BOOL
+umss_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+NTSTATUS
+umss_process_srb(
+PDEVICE_OBJECT dev_obj,
+PIRP irp );
+
+VOID
+umss_load_class_driver(
+PVOID context
+);
+
+BOOL
+umss_tsc_to_sff(
+PIO_PACKET io_packet
+);
+
+VOID
+umss_fix_sff_result(
+PIO_PACKET io_packet,
+SCSI_REQUEST_BLOCK *srb
+);
+
+PDEVICE_OBJECT
+umss_create_port_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+
+ // currently a port device is a connection point
+ // and upper driver use this to register itself
+ // with umss driver for future notification of
+ // pnp event. 2004-03-22 23:12:41
+ UCHAR dev_name[ 64 ];
+ STRING string;
+ NTSTATUS status;
+ PDEVICE_OBJECT pdev;
+ UNICODE_STRING name_string, symb_link;
+ PUMSS_PORT_DEV_EXT pdev_ext;
+
+ sprintf( dev_name, "\\Device\\usbPort_%d", ( int )0 );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &name_string, &string, TRUE );
+ pdev = NULL;
+
+ status = IoCreateDevice(
+ dev_mgr->usb_driver_obj,
+ sizeof( UMSS_PORT_DEV_EXT ),
+ &name_string,
+ FILE_USB_DEV_TYPE,
+ 0,
+ TRUE,
+ &pdev);
+
+ if( status == STATUS_SUCCESS )
+ {
+ //
+ // We do buffered io
+ //
+ pdev->Flags |= DO_BUFFERED_IO;
+
+ pdev->Flags &= ~DO_DEVICE_INITIALIZING;
+ pdev->StackSize = 2; //one for fdo, one for file device obj
+
+ pdev_ext = ( PUMSS_PORT_DEV_EXT )pdev->DeviceExtension;
+
+ pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_CLIENT_DEV;
+ pdev_ext->dev_ext_hdr.dispatch = umss_port_dispatch_routine;
+ pdev_ext->dev_ext_hdr.start_io = NULL;
+ pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
+ pdev_ext->pdriver = pdriver;
+
+ sprintf( dev_name, "\\DosDevices\\usbPort%d", ( int )0 );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoCreateSymbolicLink( &symb_link, &name_string );
+ RtlFreeUnicodeString( &symb_link );
+
+ }
+
+ RtlFreeUnicodeString( &name_string );
+ return pdev;
+}
+
+BOOL
+umss_delete_port_device(
+PDEVICE_OBJECT dev_obj
+)
+{
+ UCHAR dev_name[ 64 ];
+ STRING string;
+ UNICODE_STRING symb_link;
+
+ if( dev_obj == NULL )
+ return FALSE;
+
+ // remove the symbolic link
+ sprintf( dev_name, "\\DosDevices\\usbPort%d", ( int )0 );
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoDeleteSymbolicLink( &symb_link );
+ RtlFreeUnicodeString( &symb_link );
+
+ if( dev_obj->ReferenceCount == 0 )
+ {
+ IoDeleteDevice( dev_obj );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_delete_port_device(): port device object is removed\n" ) );
+ }
+ return TRUE;
+}
+
+NTSTATUS
+umss_port_dispatch_routine(
+PDEVICE_OBJECT pdev_obj,
+PIRP irp
+)
+// FIXME!!! there can not be sent IOCTL_SUBMIT_URB_XXX while
+// the IOCTL_SUBMIT_CDB_XXX are active. may confuse the device.
+// not resolved yet.
+// 2004-03-22 23:12:26
+{
+ KIRQL irql;
+ ULONG ctrl_code;
+ NTSTATUS status;
+ PIO_STACK_LOCATION irp_stack;
+ PUMSS_PORT_DEV_EXT pdev_ext;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+
+ USE_IRQL;
+
+ if( pdev_obj == NULL || irp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ status = STATUS_SUCCESS;
+ irp_stack = IoGetCurrentIrpStackLocation( irp );
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+
+ pdev_ext = ( PUMSS_PORT_DEV_EXT )pdev_obj->DeviceExtension;
+
+ switch( irp_stack->MajorFunction )
+ {
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ {
+ switch( ctrl_code )
+ {
+ case IOCTL_REGISTER_DRIVER:
+ {
+ PCLASS_DRV_REG_INFO pcdri;
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( CLASS_DRV_REG_INFO ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ pcdri = irp->AssociatedIrp.SystemBuffer;
+ if( pcdri->fdo_driver == NULL || pcdri->add_device == NULL || pcdri->pnp_dispatch == NULL )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext;
+ pdrvr_ext->class_driver_info.fdo_driver = pcdri->fdo_driver;
+ pdrvr_ext->class_driver_info.add_device = pcdri->add_device;
+ pdrvr_ext->class_driver_info.pnp_dispatch = pcdri->pnp_dispatch;
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_REVOKE_DRIVER:
+ {
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext;
+ pdrvr_ext->class_driver_info.fdo_driver = NULL;
+ pdrvr_ext->class_driver_info.add_device = NULL;
+ pdrvr_ext->class_driver_info.pnp_dispatch = NULL;
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ }
+ }
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLOSE:
+ {
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ }
+ EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp );
+}
+
+BOOL
+umss_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ UNICODE_STRING unicode_string;
+ NTSTATUS status;
+
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ //init driver structure, no PNP table functions
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0x0dd8; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0x0003; // USB Product ID.
+ pdriver->driver_desc.release_num = 0x100; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 1; // Configuration Value
+ pdriver->driver_desc.if_num = 1; // Interface Number
+ pdriver->driver_desc.if_class = USB_CLASS_MASS_STORAGE; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB Mass Storage driver"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_MASS_STORAGE;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( UMSS_DRVR_EXTENSION ) );
+ pdriver->driver_ext_size = sizeof( UMSS_DRVR_EXTENSION );
+
+ RtlZeroMemory( pdriver->driver_ext, sizeof( UMSS_DRVR_EXTENSION ) );
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdriver->driver_ext;
+ pdrvr_ext->dev_count = 0;
+ InitializeListHead( &pdrvr_ext->dev_list );
+ ExInitializeFastMutex( &pdrvr_ext->dev_list_mutex );
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = umss_connect;
+ pdriver->disp_tbl.dev_disconnect = umss_disconnect;
+ pdriver->disp_tbl.dev_stop = umss_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ if( ( pdrvr_ext->port_dev_obj = umss_create_port_device( dev_mgr, pdriver ) ) == NULL )
+ {
+ usb_free_mem( pdriver->driver_ext );
+ pdriver->driver_ext = NULL;
+ pdriver->driver_ext_size = 0;
+ pdriver->disp_tbl.dev_connect = NULL;
+ pdriver->disp_tbl.dev_stop = NULL;
+ pdriver->disp_tbl.dev_disconnect = NULL;
+ return FALSE;
+ }
+
+ umss_load_class_driver( NULL );
+
+ // umss_schedule_workitem( NULL, umss_load_class_driver, NULL, 0 );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_driver_init(): umss driver is initialized\n") );
+ return TRUE;
+}
+
+BOOL
+umss_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION ) pdriver->driver_ext;
+ umss_delete_port_device( pdrvr_ext->port_dev_obj );
+ pdrvr_ext->port_dev_obj = NULL;
+
+ ASSERT( IsListEmpty( &pdrvr_ext->dev_list ) == TRUE );
+ usb_free_mem( pdriver->driver_ext );
+ pdriver->driver_ext = NULL;
+ pdriver->driver_ext_size = 0;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_driver_destroy(): umss driver is destroyed\n" ) );
+ return TRUE;
+}
+
+PDEVICE_OBJECT
+umss_create_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER umss_drvr,
+DEV_HANDLE dev_handle,
+BOOL is_if
+)
+{
+
+ UCHAR dev_name[ 64 ], dev_id;
+ STRING string;
+ NTSTATUS status;
+ PDEVICE_OBJECT pdev;
+ UNICODE_STRING name_string, symb_link;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_create_device(): entering...\n" ) );
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )umss_drvr->driver_ext;
+ dev_id = ( UCHAR )dev_id_from_handle( dev_handle ); // pdrvr_ext->dev_count;
+
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\Device\\umssdev_%d", ( int )dev_id );
+ else
+ sprintf( dev_name, "\\Device\\umssifdev_%d", ( int )dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &name_string, &string, TRUE );
+ pdev = NULL;
+
+ status = IoCreateDevice(
+ dev_mgr->usb_driver_obj,
+ sizeof( UMSS_DEVICE_EXTENSION ),
+ &name_string,
+ FILE_USB_DEV_TYPE,
+ 0,
+ TRUE,
+ &pdev);
+
+ if( status == STATUS_SUCCESS )
+ {
+ //
+ // We do direct io
+ //
+ pdev->Flags |= DO_DIRECT_IO;
+
+ pdev->Flags &= ~DO_DEVICE_INITIALIZING;
+ pdev->StackSize = 2; //one for fdo, one for file device obj
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev->DeviceExtension;
+
+ //may be accessed by other thread
+ ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex );
+ InsertTailList( &pdrvr_ext->dev_list, &pdev_ext->dev_obj_link );
+ pdrvr_ext->dev_count++;
+ ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex );
+
+ if( is_if )
+ pdev_ext->flags |= UMSS_DEV_FLAG_IF_DEV;
+
+ pdev_ext->umss_dev_id = dev_id;
+ pdev_ext->pdo = pdev;
+ pdev_ext->dev_handle = dev_handle;
+ pdev_ext->dev_mgr = dev_mgr;
+ pdev_ext->pdriver = umss_drvr;
+
+ pdev_ext->dev_ext_hdr.type = NTDEV_TYPE_CLIENT_DEV;
+ pdev_ext->dev_ext_hdr.dispatch = umss_dispatch_routine;
+ pdev_ext->dev_ext_hdr.start_io = umss_start_io;
+ pdev_ext->dev_ext_hdr.dev_mgr = dev_mgr;
+
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\DosDevices\\umssdev%d", ( int )dev_id );
+ else
+ sprintf( dev_name, "\\DosDevices\\umssifdev%d", ( int )dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoCreateSymbolicLink( &symb_link, &name_string );
+ RtlFreeUnicodeString( &symb_link );
+ KeInitializeEvent( &pdev_ext->sync_event, SynchronizationEvent, FALSE );
+ KeInitializeSpinLock( &pdev_ext->dev_lock );
+
+ }
+ RtlFreeUnicodeString( &name_string );
+ return pdev;
+}
+
+BOOL
+umss_connect(
+PCONNECT_DATA param,
+DEV_HANDLE dev_handle
+)
+{
+ PURB purb;
+ NTSTATUS status;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DRIVER pdrvr;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_connect(): entering...\n" ) );
+
+ dev_mgr = param->dev_mgr;
+ pdrvr = param->pdriver;
+
+ //directly set the configuration
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ return FALSE;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+
+ purb->endp_handle = dev_handle | 0xffff;
+ purb->data_buffer = NULL;
+ purb->data_length = 0;
+ purb->completion = umss_set_cfg_completion;
+ purb->context = dev_mgr;
+ purb->reference = ( LONG )pdrvr;
+ psetup->bmRequestType = 0;
+ psetup->bRequest = USB_REQ_SET_CONFIGURATION;
+ psetup->wValue = 1;
+ psetup->wIndex = 0;
+ psetup->wLength = 0;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ return FALSE;
+ }
+ return TRUE;
+}
+
+VOID
+umss_set_cfg_completion(
+PURB purb,
+PVOID pcontext
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUCHAR buf;
+ PWORK_QUEUE_ITEM pwork_item;
+ PUMSS_CREATE_DATA pcd;
+ DEV_HANDLE dev_handle;
+ NTSTATUS status;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DRIVER pdrvr;
+
+ if( purb == NULL || pcontext == NULL )
+ return;
+
+ dev_mgr = ( PUSB_DEV_MANAGER ) pcontext;
+ pdrvr = ( PUSB_DRIVER )purb->reference;
+ dev_handle = purb->endp_handle & ~0xffff;
+
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ buf = usb_alloc_mem( NonPagedPool, 512 );
+ if( buf == NULL )
+ {
+ usb_free_mem( purb );
+ return;
+ }
+
+ //now let's get the descs, one configuration, one interface and two endpoint
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ purb->data_buffer = buf;
+ purb->data_length = 512;
+ purb->completion = NULL; //this is an immediate request, no needs completion
+ purb->context = dev_mgr;
+ purb->reference = 0;
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = USB_DT_CONFIG << 8;
+ psetup->wIndex = 0;
+ psetup->wLength = 512;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ TRAP();
+ }
+ usb_free_mem( purb );
+ purb = NULL;
+
+ if( status != STATUS_SUCCESS )
+ {
+ usb_free_mem( buf );
+ buf = NULL;
+ return;
+ }
+
+ pcd = usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_CREATE_DATA ) );
+ if( pcd == NULL )
+ {
+ usb_free_mem( buf );
+ buf = NULL;
+ return;
+ }
+
+ pcd->desc_buf = buf;
+ pcd->dev_handle = dev_handle;
+ pcd->dev_mgr = dev_mgr;
+ pcd->pdriver = pdrvr;
+ pwork_item = ( PWORK_QUEUE_ITEM )( &pcd[ 1 ] );
+
+ ExInitializeWorkItem( pwork_item, umss_start_create_device, ( PVOID )pcd );
+ ExQueueWorkItem( pwork_item, DelayedWorkQueue );
+}
+
+VOID
+umss_start_create_device(
+IN PVOID Parameter
+)
+{
+ LONG i;
+ PUCHAR desc_buf;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ DEV_HANDLE dev_handle;
+ PUSB_DRIVER pdrvr;
+ PDEVICE_OBJECT pdev_obj;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUMSS_CREATE_DATA pcd;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_ENDPOINT_DESC pendp_desc;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+
+ USE_IRQL;
+
+ if( Parameter == NULL )
+ return;
+
+ pcd = ( PUMSS_CREATE_DATA )Parameter;
+ desc_buf = pcd->desc_buf;
+ dev_mgr = pcd->dev_mgr;
+ dev_handle = pcd->dev_handle;
+ pdrvr = pcd->pdriver;
+ usb_free_mem( pcd );
+ pcd = NULL;
+
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ usb_free_mem( desc_buf );
+ return;
+ }
+
+ pdev_obj = umss_create_device( dev_mgr, pdrvr, dev_handle, FALSE );
+
+ lock_dev( pdev, FALSE );
+ if( pdev_obj == NULL || \
+ dev_state( pdev ) == USB_DEV_STATE_ZOMB || \
+ dev_mgr_set_driver( dev_mgr, dev_handle, pdrvr, pdev ) == FALSE )
+ {
+ usb_free_mem( desc_buf );
+ unlock_dev( pdev, FALSE );
+
+ if( pdev_obj )
+ umss_delete_device( dev_mgr, pdrvr, pdev_obj, FALSE );
+
+ usb_unlock_dev( pdev );
+ return;
+ }
+ unlock_dev( pdev, FALSE );
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension;
+
+ pdev_ext->desc_buf = desc_buf;
+ pdev_ext->pif_desc = NULL;
+ pdev_ext->pin_endp_desc = pdev_ext->pout_endp_desc = NULL;
+
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )desc_buf;
+ pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] );
+ //search for our if
+ for( i = 0; ( ( UCHAR ) i ) < pconfig_desc->bNumInterfaces; i++ )
+ {
+ if( pif_desc->bLength == sizeof( USB_INTERFACE_DESC ) \
+ && pif_desc->bDescriptorType == USB_DT_INTERFACE )
+ {
+ if( pif_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE )
+ {
+ pdev_ext->pif_desc = pif_desc;
+ pdev_ext->if_idx = ( UCHAR )i;
+ break;
+ }
+ else
+ {
+ if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE )
+ break;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( pdev_ext->pif_desc )
+ {
+ pendp_desc = ( PUSB_ENDPOINT_DESC )&pif_desc[ 1 ];
+ for( i = 0; ( ( UCHAR )i ) < pif_desc->bNumEndpoints; i++ )
+ {
+ if( pendp_desc->bDescriptorType == USB_DT_ENDPOINT \
+ && pendp_desc->bLength == sizeof( USB_ENDPOINT_DESC ) )
+ {
+ if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \
+ == USB_ENDPOINT_XFER_INT )
+ {
+ pdev_ext->pint_endp_desc = pendp_desc;
+ pdev_ext->int_endp_idx = ( UCHAR )i;
+ }
+ else if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \
+ == USB_ENDPOINT_XFER_BULK )
+ {
+ if( pendp_desc->bEndpointAddress & USB_DIR_IN )
+ {
+ pdev_ext->pin_endp_desc = pendp_desc;
+ pdev_ext->in_endp_idx = ( UCHAR )i;
+ }
+ else
+ {
+ pdev_ext->pout_endp_desc = pendp_desc;
+ pdev_ext->out_endp_idx = ( UCHAR )i;
+ }
+ }
+ pendp_desc = &pendp_desc[ 1 ];
+ }
+ else
+ break;
+ }
+ }
+ usb_unlock_dev( pdev );
+ return;
+}
+
+BOOL
+umss_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ return TRUE;
+}
+
+BOOL
+umss_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+{
+ PDEVICE_OBJECT dev_obj;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_DRIVER pdrvr;
+
+ if( dev_mgr == NULL || dev_handle == 0 )
+ return FALSE;
+
+ pdev = NULL;
+ //special use of the lock dev, simply use this routine to get the dev
+ status = usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev );
+ if( pdev == NULL )
+ {
+ return FALSE;
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ // must be a bug
+ TRAP();
+ usb_unlock_dev( pdev );
+ }
+ pdrvr = pdev->dev_driver;
+ dev_obj = pdev->dev_obj;
+ pdev = NULL;
+
+ return umss_delete_device( dev_mgr, pdrvr, dev_obj, FALSE );
+}
+
+VOID
+umss_deferred_delete_device(
+PVOID context
+)
+{
+ PDEVICE_OBJECT dev_obj;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ LARGE_INTEGER interval;
+
+ if( context == NULL )
+ return;
+
+ dev_obj = ( PDEVICE_OBJECT )context;
+ pdev_ext = dev_obj->DeviceExtension;
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdev_ext->pdriver->driver_ext;
+
+ interval.QuadPart = -20000; //two ms
+
+ for( ; ; )
+ {
+ if( dev_obj->ReferenceCount )
+ KeDelayExecutionThread( KernelMode, TRUE, &interval );
+ else
+ {
+ KeDelayExecutionThread( KernelMode, TRUE, &interval );
+ if( dev_obj->ReferenceCount == 0 )
+ break;
+ }
+ }
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_deferred_delete_device(): delete device, 0x%x\n", dev_obj ) );
+
+ ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex );
+ RemoveEntryList( &pdev_ext->dev_obj_link );
+ pdrvr_ext->dev_count--;
+ ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex );
+
+ IoDeleteDevice( dev_obj );
+ return;
+}
+
+BOOL
+umss_delete_device(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdrvr,
+PDEVICE_OBJECT dev_obj,
+BOOL is_if
+)
+{
+ UCHAR dev_name[ 64 ];
+ STRING string;
+ UNICODE_STRING symb_link;
+
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+
+ if( dev_obj == NULL )
+ return FALSE;
+
+ if( pdrvr == NULL || dev_mgr == NULL )
+ return FALSE;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension;
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext;
+ if( is_if == FALSE )
+ sprintf( dev_name, "\\DosDevices\\umssdev%d", ( int )pdev_ext->umss_dev_id );
+ else
+ sprintf( dev_name, "\\DosDevices\\umssifdev%d", ( int )pdev_ext->umss_dev_id );
+
+ RtlInitString( &string, dev_name );
+ RtlAnsiStringToUnicodeString( &symb_link, &string, TRUE );
+ IoDeleteSymbolicLink( &symb_link );
+ RtlFreeUnicodeString( &symb_link );
+
+ if( pdev_ext->desc_buf )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_delete_device(): delete desc_buf\n" ) );
+ usb_free_mem( pdev_ext->desc_buf );
+ pdev_ext->desc_buf = NULL;
+
+ }
+
+ if( dev_obj->ReferenceCount == 0 )
+ {
+ ExAcquireFastMutex( &pdrvr_ext->dev_list_mutex );
+ RemoveEntryList( &pdev_ext->dev_obj_link );
+ pdrvr_ext->dev_count--;
+ ExReleaseFastMutex( &pdrvr_ext->dev_list_mutex );
+
+ IoDeleteDevice( dev_obj );
+ return TRUE;
+ }
+
+ //
+ // FIXME: how if the driver unloading happens
+ // since this is called in dev_mgr_disconnect_dev, umss_schedule_workitem
+ // can not protect the USB_DEV object from be deleted. so the workitem
+ // can not access anything relative to the USB_DEV object. In this case
+ // we will tollerate the usb_query_and_lock_dev failure since it will
+ // never success when come to this point, and we won't pass dev_mgr
+ // and pdev to the function. But other scenarios, usb_query_and_lock_dev
+ // can not fail if dev_mgr and pdev are passed valid.
+ // When driver is unloading, don't know. Wish NT will unload the driver
+ // only when all the devices to the driver are deleted.
+ //
+ umss_schedule_workitem( dev_obj, umss_deferred_delete_device, NULL, 0 );
+ return TRUE;
+}
+
+VOID
+umss_submit_io_packet(
+PDEVICE_OBJECT dev_obj,
+PIO_PACKET io_packet
+)
+{
+ NTSTATUS status;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PUSB_DEV pdev;
+
+ pdev_ext = dev_obj->DeviceExtension;
+
+ // lock the dev, the pdev_ext->pif_desc won't go away.
+ if( ( status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) )
+ != STATUS_SUCCESS )
+ {
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_start_io(): error, device is not valid\n" ) );
+ UMSS_COMPLETE_START_IO( dev_obj, status, io_packet->pirp );
+ return;
+ }
+
+ if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_BULKONLY )
+ {
+ status = umss_bulkonly_startio( pdev_ext, io_packet );
+ }
+ else if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CB
+ || pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI )
+ {
+ status = umss_cbi_startio( pdev_ext, io_packet );
+ }
+ else
+ {
+ status = STATUS_DEVICE_PROTOCOL_ERROR;
+ }
+ usb_unlock_dev( pdev );
+ UMSS_COMPLETE_START_IO( dev_obj, status, io_packet->pirp );
+ return;
+}
+
+VOID
+umss_start_io(
+IN PDEVICE_OBJECT dev_obj,
+IN PIRP irp
+)
+{
+ KIRQL irql;
+ ULONG ctrl_code;
+ NTSTATUS status;
+ PIO_STACK_LOCATION irp_stack;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ IO_PACKET io_packet;
+ PUSER_IO_PACKET user_io_packet;
+
+ USE_IRQL;
+
+ if( dev_obj == NULL || irp == NULL )
+ return;
+
+ status = STATUS_SUCCESS;
+
+ irp_stack = IoGetCurrentIrpStackLocation( irp );
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension;
+
+ if( irp_stack->MajorFunction == IRP_MJ_SCSI )
+ {
+ umss_process_srb( dev_obj, irp );
+ return;
+ }
+
+ if( irp_stack->MajorFunction != IRP_MJ_DEVICE_CONTROL )
+ {
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp );
+ }
+
+ switch( ctrl_code )
+ {
+ case IOCTL_UMSS_SUBMIT_CDB_IN:
+ case IOCTL_UMSS_SUBMIT_CDB_OUT:
+ case IOCTL_UMSS_SUBMIT_CDB:
+ {
+ PUSB_DEV pdev;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( USER_IO_PACKET ) )
+ {
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+ }
+
+ user_io_packet = ( PUSER_IO_PACKET )irp->AssociatedIrp.SystemBuffer;
+
+ if( user_io_packet->sub_class != pdev_ext->pif_desc->bInterfaceSubClass )
+ {
+ // not agree with the dev's subclass
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_DEVICE_PROTOCOL_ERROR, irp );
+ }
+
+ RtlZeroMemory( &io_packet, sizeof( io_packet ) );
+ io_packet.cdb_length = user_io_packet->cdb_length;
+ io_packet.lun = user_io_packet->lun;
+
+ RtlCopyMemory( io_packet.cdb, user_io_packet->cdb, MAX_CDB_LENGTH );
+
+ if( ctrl_code == IOCTL_UMSS_SUBMIT_CDB_IN )
+ io_packet.flags |= USB_DIR_IN;
+
+ if( ctrl_code != IOCTL_UMSS_SUBMIT_CDB )
+ {
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength == 0 )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp );
+
+ io_packet.data_buffer = MmGetSystemAddressForMdl( irp->MdlAddress );
+ io_packet.data_length = irp_stack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if( io_packet.data_length > MAX_BULK_TRANSFER_LENGTH )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp );
+
+ //synchronize the buffer
+ if( io_packet.flags & USB_DIR_IN )
+ KeFlushIoBuffers( irp->MdlAddress, TRUE, TRUE );
+ else
+ KeFlushIoBuffers( irp->MdlAddress, FALSE, TRUE );
+ }
+
+ io_packet.pirp = irp;
+ umss_submit_io_packet( dev_obj, &io_packet );
+ return;
+ }
+ case IOCTL_SCSI_PASS_THROUGH:
+ {
+ PSCSI_PASS_THROUGH pass_through;
+ IO_PACKET io_packet;
+
+ pass_through = irp->AssociatedIrp.SystemBuffer;
+
+ if( pass_through->DataTransferLength &&
+ pass_through->DataBufferOffset != sizeof( SCSI_PASS_THROUGH ) )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+
+ if( pass_through->SenseInfoLength &&
+ ( pass_through->SenseInfoOffset !=
+ pass_through->DataBufferOffset + pass_through->DataTransferLength ) )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength <
+ ( sizeof( SCSI_PASS_THROUGH ) +
+ pass_through->SenseInfoLength +
+ pass_through->DataTransferLength ) )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp );
+
+ RtlZeroMemory( &io_packet, sizeof( io_packet ) );
+
+ io_packet.flags |= IOP_FLAG_SCSI_CTRL_TRANSFER;
+ if( pass_through->DataIn )
+ io_packet.flags |= IOP_FLAG_DIR_IN;
+
+ io_packet.data_buffer = ( PVOID )&pass_through[ 1 ];
+ io_packet.data_length = pass_through->DataTransferLength;
+
+ if( pass_through->SenseInfoLength )
+ {
+ io_packet.sense_data = ( ( PUCHAR )pass_through ) + pass_through->SenseInfoOffset;
+ io_packet.sense_data_length = pass_through->SenseInfoLength;
+ io_packet.flags |= IOP_FLAG_REQ_SENSE;
+ }
+
+ io_packet.cdb_length = pass_through->CdbLength;
+ RtlCopyMemory( io_packet.cdb, pass_through->Cdb, sizeof( io_packet.cdb ) );
+ io_packet.lun = 0;
+ io_packet.pirp = irp;
+ umss_submit_io_packet( dev_obj, &io_packet );
+ return;
+ }
+ case IOCTL_SCSI_PASS_THROUGH_DIRECT:
+ {
+ PSCSI_PASS_THROUGH_DIRECT pass_through_direct;
+ IO_PACKET io_packet;
+
+ pass_through_direct = irp->AssociatedIrp.SystemBuffer;
+
+ if( pass_through_direct->SenseInfoLength &&
+ pass_through_direct->SenseInfoOffset != sizeof( SCSI_PASS_THROUGH_DIRECT ) )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_PARAMETER, irp );
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof( SCSI_PASS_THROUGH_DIRECT ) + pass_through_direct->SenseInfoLength )
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_BUFFER_TOO_SMALL, irp );
+
+ RtlZeroMemory( &io_packet, sizeof( io_packet ) );
+
+ io_packet.flags |= IOP_FLAG_SCSI_CTRL_TRANSFER;
+ if( pass_through_direct->DataIn )
+ io_packet.flags |= IOP_FLAG_DIR_IN;
+
+ io_packet.data_buffer = pass_through_direct->DataBuffer;
+ io_packet.data_length = pass_through_direct->DataTransferLength;
+
+ if( pass_through_direct->SenseInfoLength )
+ {
+ io_packet.sense_data = ( ( PUCHAR )pass_through_direct ) + pass_through_direct->SenseInfoOffset;
+ io_packet.sense_data_length = pass_through_direct->SenseInfoLength;
+ io_packet.flags |= IOP_FLAG_REQ_SENSE;
+ }
+
+ io_packet.cdb_length = pass_through_direct->CdbLength;
+ RtlCopyMemory( io_packet.cdb, pass_through_direct->Cdb, sizeof( io_packet.cdb ) );
+ io_packet.lun = 0;
+ io_packet.pirp = irp;
+ umss_submit_io_packet( dev_obj, &io_packet );
+ return;
+ }
+ case IOCTL_SUBMIT_URB_RD:
+ case IOCTL_SUBMIT_URB_NOIO:
+ case IOCTL_SUBMIT_URB_WR:
+ {
+ gendrv_startio( dev_obj, irp );
+ return;
+ }
+ default:
+ UMSS_COMPLETE_START_IO( dev_obj, STATUS_INVALID_DEVICE_REQUEST, irp );
+ }
+ return;
+}
+
+NTSTATUS
+umss_dispatch_routine(
+PDEVICE_OBJECT pdev_obj,
+PIRP irp
+)
+// bugbug!!! there can not be sent IOCTL_SUBMIT_URB_XXX while
+// the IOCTL_SUBMIT_CDB_XXX are active. may confuse the device.
+// not resolved yet.
+{
+ KIRQL irql;
+ ULONG ctrl_code;
+ NTSTATUS status;
+ PIO_STACK_LOCATION irp_stack;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ USE_IRQL;
+
+ if( pdev_obj == NULL || irp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ status = STATUS_SUCCESS;
+ irp_stack = IoGetCurrentIrpStackLocation (irp);
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension;
+
+ switch( irp_stack->MajorFunction )
+ {
+ case IRP_MJ_CREATE:
+ case IRP_MJ_CLOSE:
+ {
+ return dev_mgr_dispatch( pdev_ext->dev_mgr, irp );
+ }
+ case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+ {
+ // function code to receive scsi request
+ UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp );
+ }
+ case IRP_MJ_DEVICE_CONTROL:
+ {
+ switch( ctrl_code )
+ {
+ case IOCTL_UMSS_SET_FDO:
+ {
+ PDEVICE_OBJECT fdo;
+ PUSB_DEV pdev;
+ USE_IRQL;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( PDEVICE_OBJECT ) ) {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ fdo = ( PDEVICE_OBJECT )( ( PULONG )irp->AssociatedIrp.SystemBuffer )[ 0 ];
+ if( fdo == NULL )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ //
+ // we have to test the usb dev's state to determine whether set or not the fdo
+ //
+
+ if( usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) != STATUS_SUCCESS )
+ EXIT_DISPATCH( STATUS_DEVICE_DOES_NOT_EXIST, irp );
+
+ lock_dev( pdev, FALSE );
+
+ if( dev_state( pdev ) >= USB_DEV_STATE_BEFORE_ZOMB ||
+ dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ EXIT_DISPATCH( STATUS_DEVICE_DOES_NOT_EXIST, irp );
+ }
+
+ pdev_ext->fdo = fdo;
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ irp->IoStatus.Information = 0;
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+
+ case IOCTL_GET_DEV_DESC:
+ {
+ PGET_DEV_DESC_REQ pgddr;
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( GET_DEV_DESC_REQ ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ pgddr = irp->AssociatedIrp.SystemBuffer;
+ if( pgddr->dev_handle != ( pdev_ext->dev_handle & 0xffff0000 ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ // an immediate request
+ return dev_mgr_dispatch( pdev_ext->dev_mgr, irp );
+ }
+ case IOCTL_SUBMIT_URB_RD:
+ case IOCTL_SUBMIT_URB_NOIO:
+ case IOCTL_SUBMIT_URB_WR:
+ {
+ PURB purb;
+ DEV_HANDLE endp_handle;
+
+ if( irp_stack->Parameters.DeviceIoControl.InputBufferLength < sizeof( URB ) )
+ {
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+
+ purb = ( PURB )irp->AssociatedIrp.SystemBuffer;
+ endp_handle = purb->endp_handle;
+ if( !default_endp_handle( endp_handle ) )
+ {
+ //no permit to other interface if interface dev
+ if( ( pdev_ext->flags & UMSS_DEV_FLAG_IF_DEV )
+ && if_idx_from_handle( endp_handle ) != pdev_ext->if_idx )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+ }
+ // FIXME: this is dangeous
+ // return dev_mgr_dispatch( pdev_ext->dev_mgr, irp );
+ UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp );
+ }
+ case IOCTL_GET_DEV_HANDLE:
+ {
+ PUCHAR user_buffer;
+ ULONG user_buffer_length;
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( LONG ) )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+
+ *( ( PLONG )irp->AssociatedIrp.SystemBuffer ) = pdev_ext->dev_handle;
+ irp->IoStatus.Information = sizeof( LONG );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+
+ //
+ // request from scsi class driver
+ //
+ case IOCTL_SCSI_PASS_THROUGH:
+ case IOCTL_SCSI_PASS_THROUGH_DIRECT:
+ //
+ // direct cdb request
+ //
+ case IOCTL_UMSS_SUBMIT_CDB:
+ case IOCTL_UMSS_SUBMIT_CDB_OUT:
+ case IOCTL_UMSS_SUBMIT_CDB_IN:
+ {
+ UMSS_EXIT_DISPATCH( pdev_obj, STATUS_PENDING, irp );
+ }
+ case IOCTL_SCSI_GET_INQUIRY_DATA:
+ {
+ PSCSI_ADAPTER_BUS_INFO adapter_info;
+ PSCSI_BUS_DATA bus_data;
+ PSCSI_INQUIRY_DATA inq_dat;
+ PINQUIRYDATA inq;
+ IO_PACKET io_packet;
+ ULONG required_size;
+
+ required_size = sizeof( SCSI_ADAPTER_BUS_INFO )
+ + sizeof( SCSI_BUS_DATA )
+ + sizeof( SCSI_INQUIRY_DATA )
+ + INQUIRYDATABUFFERSIZE;
+
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < required_size )
+ UMSS_EXIT_DISPATCH( pdev_obj, STATUS_BUFFER_TOO_SMALL, irp );
+
+ RtlZeroMemory( &io_packet, sizeof( io_packet ) );
+
+ adapter_info = irp->AssociatedIrp.SystemBuffer;
+ adapter_info->NumberOfBuses = 1;
+ bus_data = &adapter_info->BusData[ 0 ];
+ bus_data->NumberOfLogicalUnits = 1;
+ bus_data->InitiatorBusId = 0;
+ bus_data->InquiryDataOffset = sizeof( SCSI_ADAPTER_BUS_INFO );
+ inq_dat = ( PVOID )&bus_data[ 1 ];
+ inq_dat->PathId = 0;
+ inq_dat->TargetId = pdev_ext->umss_dev_id;
+ //
+ // this is the dev_id for usb dev_manager
+ //
+ inq_dat->Lun = ( UCHAR )( pdev_ext->dev_handle >> 16 );
+ inq_dat->DeviceClaimed = FALSE;
+ inq_dat->InquiryDataLength = 36;
+ inq_dat->NextInquiryDataOffset = 0;
+ inq = ( PINQUIRYDATA )inq_dat->InquiryData;
+
+ RtlZeroMemory( inq, sizeof( INQUIRYDATA ) );
+ inq->DeviceType = DIRECT_ACCESS_DEVICE;
+ inq->DeviceTypeQualifier = 0;
+ inq->RemovableMedia = 1;
+
+ //
+ // pretend to comply scsi primary 2 command set
+ //
+
+ inq->Versions = 0x04;
+
+ //
+ // the format is in scsi-2 format
+ //
+
+ inq->ResponseDataFormat = 0x02;
+
+ //
+ // we are the poor scsi device
+ //
+
+ inq->AdditionalLength = 31;
+ inq->SoftReset = 0;
+ inq->CommandQueue = 0;
+ inq->LinkedCommands = 0;
+ inq->RelativeAddressing = 0;
+ RtlCopyMemory( &inq->VendorId, "Unknown", 7 );
+ RtlCopyMemory( &inq->ProductId, "USB Mass Storage", 16 );
+ irp->IoStatus.Information = required_size;
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_SCSI_GET_CAPABILITIES:
+ {
+ PIO_SCSI_CAPABILITIES port_cap;
+
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( IO_SCSI_CAPABILITIES ) )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+
+ port_cap = ( PIO_SCSI_CAPABILITIES ) irp->AssociatedIrp.SystemBuffer;
+ port_cap->Length = sizeof( IO_SCSI_CAPABILITIES );
+ port_cap->MaximumTransferLength = 65536;
+ port_cap->MaximumPhysicalPages = 65536 / PAGE_SIZE;
+ port_cap->SupportedAsynchronousEvents = 0;
+ port_cap->AlignmentMask = 0x10;
+ port_cap->TaggedQueuing = FALSE;
+ port_cap->AdapterScansDown = FALSE;
+ port_cap->AdapterUsesPio = FALSE;
+ irp->IoStatus.Information = sizeof( IO_SCSI_CAPABILITIES );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_SCSI_GET_ADDRESS:
+ {
+ PSCSI_ADDRESS paddr;
+ if( irp_stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( SCSI_ADDRESS ) )
+ EXIT_DISPATCH( STATUS_INVALID_PARAMETER, irp );
+
+ paddr = ( PSCSI_ADDRESS )irp->AssociatedIrp.SystemBuffer;
+
+ paddr->Length = sizeof( SCSI_ADDRESS );
+ paddr->PortNumber = 0;
+ paddr->PathId = 0;
+ paddr->TargetId = pdev_ext->umss_dev_id;
+ paddr->Lun = ( UCHAR )( pdev_ext->dev_handle >> 16 );
+ irp->IoStatus.Information = sizeof( SCSI_ADDRESS );
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ case IOCTL_SCSI_RESCAN_BUS:
+ {
+ irp->IoStatus.Information = 0;
+ EXIT_DISPATCH( STATUS_SUCCESS, irp );
+ }
+ default:
+ {
+ EXIT_DISPATCH( STATUS_INVALID_DEVICE_REQUEST, irp );
+ }
+ }
+ }
+ }
+ EXIT_DISPATCH( STATUS_NOT_SUPPORTED, irp );
+}
+
+VOID
+umss_reset_pipe_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ if( context == NULL )
+ return;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )context;
+ pdev_ext->reset_pipe_status = purb->status;
+ KeSetEvent( &pdev_ext->sync_event, 0, FALSE );
+ return;
+}
+
+NTSTATUS
+umss_reset_pipe(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+DEV_HANDLE endp_handle
+)
+//can only be called at passive level
+{
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+
+ if( pdev_ext == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev );
+
+ if( status != STATUS_SUCCESS )
+ return STATUS_UNSUCCESSFUL;
+
+ status = usb_reset_pipe_ex(
+ pdev_ext->dev_mgr,
+ endp_handle,
+ umss_reset_pipe_completion,
+ pdev_ext );
+
+ if( status == STATUS_PENDING )
+ {
+ KeWaitForSingleObject(
+ &pdev_ext->sync_event,
+ Executive,
+ KernelMode,
+ TRUE,
+ NULL );
+ status = pdev_ext->reset_pipe_status;
+ }
+ usb_unlock_dev( pdev );
+ return status;
+}
+
+BOOL
+umss_gen_result_srb(
+PIO_PACKET io_packet,
+PSCSI_REQUEST_BLOCK srb,
+NTSTATUS status
+)
+{
+
+ if( srb == NULL || io_packet == NULL )
+ {
+ return FALSE;
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ PULONG dest_buf, src_buf;
+ ULONG i;
+
+ srb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ io_packet->pirp->IoStatus.Information = srb->DataTransferLength;
+ if( ( io_packet->pirp->Flags & IRP_READ_OPERATION ) && !( io_packet->pirp->Flags & IRP_PAGING_IO ) )
+ {
+ src_buf = ( PULONG )io_packet->data_buffer;
+ dest_buf = ( PULONG )srb->DataBuffer;
+ if( src_buf && dest_buf )
+ {
+ for( i = 0; i < ( srb->DataTransferLength >> 2 ); i++ )
+ {
+ dest_buf[ i ] = src_buf[ i ];
+ }
+ }
+ }
+ }
+ else if( status == STATUS_DEVICE_DOES_NOT_EXIST )
+ {
+ PSENSE_DATA sense_buf;
+ srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ //
+ // let's build the srb status for class driver
+ //
+
+ srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer;
+
+ if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) )
+ {
+ RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength );
+ sense_buf->ErrorCode = 0x70;
+ sense_buf->Valid = 1;
+ sense_buf->SenseKey = SCSI_SENSE_NOT_READY;
+ sense_buf->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ sense_buf->AdditionalSenseLength = 10;
+ }
+ }
+ else if( status == USB_STATUS_STALL_PID || status == USB_STATUS_CRC ||
+ status == USB_STATUS_BTSTUFF || status == USB_STATUS_DATA_OVERRUN )
+ {
+ PSENSE_DATA sense_buf;
+ srb->SrbStatus = SRB_STATUS_ERROR;
+ srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer;
+
+ if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) )
+ {
+ RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength );
+ sense_buf->ErrorCode = 0x70;
+ sense_buf->Valid = 1;
+ sense_buf->SenseKey = SCSI_SENSE_HARDWARE_ERROR;
+ sense_buf->AdditionalSenseCode = 0;
+ sense_buf->AdditionalSenseLength = 10;
+ }
+ }
+ else
+ {
+ srb->SrbStatus = SRB_STATUS_ERROR;
+ }
+
+ if( ( io_packet->pirp->Flags & ( IRP_READ_OPERATION | IRP_WRITE_OPERATION ) ) && !( io_packet->pirp->Flags & IRP_PAGING_IO ) )
+ {
+ if( io_packet->data_buffer )
+ {
+ usb_free_mem( io_packet->data_buffer );
+ io_packet->data_buffer = NULL;
+ }
+ }
+ return TRUE;
+}
+
+BOOL
+umss_gen_result_ctrl(
+PDEVICE_OBJECT dev_obj,
+PIRP irp,
+NTSTATUS status
+)
+{
+ PIO_STACK_LOCATION irp_stack;
+ ULONG ctrl_code;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ if( irp == NULL )
+ return FALSE;
+
+ irp->IoStatus.Information = 0;
+ irp_stack = IoGetCurrentIrpStackLocation ( irp );
+ ctrl_code = irp_stack->Parameters.DeviceIoControl.IoControlCode;
+ pdev_ext = dev_obj->DeviceExtension;
+
+ switch( ctrl_code )
+ {
+ case IOCTL_SCSI_PASS_THROUGH:
+ {
+ PSCSI_PASS_THROUGH pass_through;
+ pass_through = irp->AssociatedIrp.SystemBuffer;
+ irp->IoStatus.Status = status;
+
+ // we have set these two value in bulkonly.c when data transfer complete
+ // pass_through_direct->DataTransferLength = pdev_ext->io_packet.data_length;
+ // pass_through_direct->SenseInfoLength = pdev_ext->io_packet.sense_data_length;
+
+ if( status == STATUS_SUCCESS )
+ irp->IoStatus.Information = pass_through->SenseInfoOffset + pass_through->SenseInfoLength;
+ else
+ pass_through->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return TRUE;
+ }
+ case IOCTL_SCSI_PASS_THROUGH_DIRECT:
+ {
+ PSCSI_PASS_THROUGH_DIRECT pass_through_direct;
+
+ pass_through_direct = irp->AssociatedIrp.SystemBuffer;
+ pass_through_direct->ScsiStatus = 0;
+ irp->IoStatus.Status = status;
+
+ // we have set these two value in bulkonly.c when data transfer complete
+ // pass_through_direct->DataTransferLength = pdev_ext->io_packet.data_length;
+ // pass_through_direct->SenseInfoLength = pdev_ext->io_packet.sense_data_length;
+
+ if( status == STATUS_SUCCESS )
+ irp->IoStatus.Information = pass_through_direct->SenseInfoOffset + pass_through_direct->SenseInfoLength;
+ else
+ pass_through_direct->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+VOID
+umss_complete_request(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+NTSTATUS status
+)
+{
+ PIRP pirp;
+ KIRQL old_irql;
+
+ PDEVICE_OBJECT dev_obj;
+ PIO_STACK_LOCATION irp_stack;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_complete_request(): entering...\n" ) );
+
+ pirp = pdev_ext->io_packet.pirp;
+ dev_obj = pdev_ext->pdo;
+
+ irp_stack = IoGetCurrentIrpStackLocation ( pirp );
+
+ if( pdev_ext->io_packet.flags & IOP_FLAG_SRB_TRANSFER )
+ {
+ if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I )
+ {
+ umss_fix_sff_result( &pdev_ext->io_packet, irp_stack->Parameters.Scsi.Srb );
+ }
+ umss_gen_result_srb( &pdev_ext->io_packet, irp_stack->Parameters.Scsi.Srb, status );
+ }
+ else if( pdev_ext->io_packet.flags & IOP_FLAG_SCSI_CTRL_TRANSFER )
+ umss_gen_result_ctrl( dev_obj, pirp, status );
+
+ //this device has its irp queued
+ if( status == STATUS_CANCELLED )
+ {
+ IoAcquireCancelSpinLock( &old_irql );
+ if( dev_obj->CurrentIrp == pirp )
+ {
+ IoReleaseCancelSpinLock( old_irql );
+ IoStartNextPacket( dev_obj, FALSE );
+ }
+ else
+ {
+ KeRemoveEntryDeviceQueue( &dev_obj->DeviceQueue, &pirp->Tail.Overlay.DeviceQueueEntry );
+ IoReleaseCancelSpinLock( old_irql );
+ }
+ }
+ else
+ // all requests come to this point from the irp queue
+ IoStartNextPacket( dev_obj, FALSE );
+
+ pirp->IoStatus.Status = status;
+
+ if( status != STATUS_SUCCESS )
+ pirp->IoStatus.Information = 0;
+
+ IoCompleteRequest( pirp, IO_NO_INCREMENT );
+ return;
+}
+
+BOOL
+umss_if_connect(
+PCONNECT_DATA params,
+DEV_HANDLE if_handle
+)
+{
+ PURB purb;
+ LONG if_idx, i;
+ PUCHAR desc_buf;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_DRIVER pdrvr;
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PUSB_CONFIGURATION_DESC pconfig_desc;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_ENDPOINT_DESC pendp_desc;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+
+ PDEVICE_OBJECT pdev_obj;
+ USE_IRQL;
+ //configuration is already set
+ purb = NULL;
+ desc_buf = NULL;
+ pdev = NULL;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_if_connect(): entering...\n" ) );
+
+ if( params == NULL )
+ return FALSE;
+
+ dev_mgr = params->dev_mgr;
+ pdrvr = params->pdriver;
+
+ if_idx = if_idx_from_handle( if_handle );
+
+ purb = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb == NULL )
+ goto ERROR_OUT;
+
+ desc_buf = usb_alloc_mem( NonPagedPool, 512 );
+ if( desc_buf == NULL )
+ goto ERROR_OUT;
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ urb_init( ( purb ) );
+
+ // now let's get the descs, one configuration, one interface and two endpoint
+ psetup = ( PUSB_CTRL_SETUP_PACKET )( purb )->setup_packet;
+ purb->endp_handle = if_handle | 0xffff;
+ purb->data_buffer = desc_buf;
+ purb->data_length = 512;
+ purb->completion = NULL; // this is an immediate request, no needs completion
+ purb->context = dev_mgr;
+ purb->reference = 0;
+ psetup->bmRequestType = 0x80;
+ psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
+ psetup->wValue = USB_DT_CONFIG << 8;
+ psetup->wIndex = 0;
+ psetup->wLength = 512;
+
+ status = usb_submit_urb( dev_mgr, purb );
+ if( status == STATUS_PENDING )
+ {
+ TRAP();
+ }
+ usb_free_mem( purb );
+ purb = NULL;
+
+ if( status != STATUS_SUCCESS )
+ {
+ goto ERROR_OUT;
+ }
+
+ status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ goto ERROR_OUT;
+ }
+
+#ifdef _TIANSHENG_DRIVER
+ if( !( ( pdev->pusb_dev_desc->idVendor == 0x03eb && pdev->pusb_dev_desc->idProduct == 0x2002 )
+ ||( pdev->pusb_dev_desc->idVendor == 0x0ea0 && pdev->pusb_dev_desc->idProduct == 0x6803 )
+ ||( pdev->pusb_dev_desc->idVendor == 0x0ef5 && pdev->pusb_dev_desc->idProduct == 0x2202 ) ) )
+ {
+ // check TianSheng's product
+ goto ERROR_OUT;
+ }
+#endif
+
+ pdev_obj = umss_create_device( dev_mgr, pdrvr, if_handle, TRUE );
+ if( pdev_obj == NULL )
+ {
+ goto ERROR_OUT;
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB ||
+ dev_mgr_set_if_driver( dev_mgr, if_handle, pdrvr, pdev ) == FALSE )
+ {
+ unlock_dev( pdev, FALSE );
+ if( pdev_obj )
+ {
+ umss_delete_device( dev_mgr, pdrvr, pdev_obj, TRUE );
+ }
+ goto ERROR_OUT;
+ }
+
+ if( pdev->usb_config )
+ {
+ pdev->usb_config->interf[ if_idx ].if_ext = pdev_obj;
+ pdev->usb_config->interf[ if_idx ].if_ext_size = 0;
+ }
+ // olympus dev needs special care
+ if( UMSS_OLYMPUS_VENDOR_ID == pdev->pusb_dev_desc->idVendor )
+ status = TRUE;
+ else
+ status = FALSE;
+
+ unlock_dev( pdev, FALSE );
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )pdev_obj->DeviceExtension;
+
+ pdev_ext->desc_buf = desc_buf;
+ pdev_ext->pif_desc = NULL;
+ pdev_ext->pin_endp_desc = pdev_ext->pout_endp_desc = NULL;
+ pconfig_desc = ( PUSB_CONFIGURATION_DESC )desc_buf;
+ pif_desc = ( PUSB_INTERFACE_DESC )( &pconfig_desc[ 1 ] );
+
+ if( status )
+ pdev_ext->flags |= UMSS_DEV_FLAG_OLYMPUS_DEV;
+
+ //search for our if
+ for( i = 0; ( ( UCHAR ) i ) < if_idx; i++ )
+ {
+ if( usb_skip_if_and_altif( ( PUCHAR* )&pif_desc ) == FALSE )
+ break;
+ }
+ pdev_ext->pif_desc = pif_desc;
+
+ if( pdev_ext->pif_desc )
+ {
+ pendp_desc = ( PUSB_ENDPOINT_DESC )&pif_desc[ 1 ];
+ for( i = 0; ( ( UCHAR )i ) < pif_desc->bNumEndpoints; i++ )
+ {
+ if( pendp_desc->bDescriptorType == USB_DT_ENDPOINT \
+ && pendp_desc->bLength == sizeof( USB_ENDPOINT_DESC ) )
+ {
+ if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \
+ == USB_ENDPOINT_XFER_INT )
+ {
+ pdev_ext->pint_endp_desc = pendp_desc;
+ pdev_ext->int_endp_idx = ( UCHAR )i;
+ }
+ else if( ( pendp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) \
+ == USB_ENDPOINT_XFER_BULK )
+ {
+ if( pendp_desc->bEndpointAddress & USB_DIR_IN )
+ {
+ pdev_ext->pin_endp_desc = pendp_desc;
+ pdev_ext->in_endp_idx = ( UCHAR )i;
+ }
+ else
+ {
+ pdev_ext->pout_endp_desc = pendp_desc;
+ pdev_ext->out_endp_idx = ( UCHAR )i;
+ }
+ }
+ pendp_desc = &pendp_desc[ 1 ];
+ }
+ else
+ break;
+ }
+ }
+
+ // notify the class driver, some device comes
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext;
+ if( pdrvr_ext &&
+ pdrvr_ext->class_driver_info.add_device &&
+ pdrvr_ext->class_driver_info.fdo_driver )
+ pdrvr_ext->class_driver_info.add_device( pdrvr_ext->class_driver_info.fdo_driver, pdev_obj );
+
+ usb_unlock_dev( pdev );
+ return TRUE;
+
+ERROR_OUT:
+
+ if( desc_buf )
+ usb_free_mem( desc_buf );
+
+ if( purb )
+ usb_free_mem( purb );
+
+ usb_unlock_dev( pdev );
+
+ desc_buf = NULL;
+ purb = NULL;
+
+ return FALSE;
+}
+
+BOOL
+umss_if_disconnect(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle
+)
+{
+ LONG if_idx;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_DRIVER pdrvr;
+ PDEVICE_OBJECT dev_obj;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ if( dev_mgr == NULL || if_handle == 0 )
+ return FALSE;
+
+ pdev = NULL;
+ if_idx = if_idx_from_handle( if_handle );
+ //
+ // special use of the lock dev, simply use this routine to get the dev
+ //
+ status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev );
+ if( pdev == NULL )
+ {
+ return FALSE;
+ }
+ if( status == STATUS_SUCCESS )
+ {
+ // must be a bug
+ TRAP();
+ }
+ if( pdev->usb_config )
+ {
+ pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv;
+ dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext;
+ }
+ pdev = NULL;
+
+ // notify the class driver, some device gone
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext;
+ pdev_ext = dev_obj->DeviceExtension;
+ if( pdrvr_ext && pdrvr_ext->class_driver_info.pnp_dispatch )
+ pdrvr_ext->class_driver_info.pnp_dispatch( dev_obj, UMSS_PNPMSG_DISCONNECT, NULL );
+
+ // no need to unlock the dev
+ return umss_delete_device( dev_mgr, pdrvr, dev_obj, TRUE );
+}
+
+BOOL
+umss_if_stop(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE if_handle
+)
+{
+ LONG if_idx;
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PUSB_DRIVER pdrvr;
+ PDEVICE_OBJECT dev_obj;
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+
+ USE_IRQL;
+
+ if( dev_mgr == NULL || if_handle == 0 )
+ return FALSE;
+
+ pdev = NULL;
+ if_idx = if_idx_from_handle( if_handle );
+
+ // special use of the lock dev, simply use this routine to get the dev
+ status = usb_query_and_lock_dev( dev_mgr, if_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ return FALSE;
+ }
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ return FALSE;
+ }
+
+ if( pdev->usb_config )
+ {
+ pdrvr = pdev->usb_config->interf[ if_idx ].pif_drv;
+ dev_obj = ( PDEVICE_OBJECT )pdev->usb_config->interf[ if_idx ].if_ext;
+ }
+ unlock_dev( pdev, FALSE );
+
+ // notify the class driver, some device stops
+ pdev_ext = dev_obj->DeviceExtension;
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdrvr->driver_ext;
+ if( pdrvr_ext && pdrvr_ext->class_driver_info.pnp_dispatch )
+ pdrvr_ext->class_driver_info.pnp_dispatch( dev_obj, UMSS_PNPMSG_STOP, NULL );
+
+ usb_unlock_dev( pdev );
+ return TRUE;
+}
+
+VOID
+umss_load_class_driver(
+PVOID context
+)
+{
+ NTSTATUS status;
+ UNICODE_STRING unicode_string;
+
+ //
+ // let's load the class driver
+ //
+ RtlInitUnicodeString(&unicode_string, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\usbstor");
+ status = ZwLoadDriver( &unicode_string );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_load_class_driver(): try to load class driver, status=0x%x\n", status ) );
+}
+BOOL
+umss_if_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+)
+{
+ PUMSS_DRVR_EXTENSION pdrvr_ext;
+ UNICODE_STRING unicode_string;
+ NTSTATUS status;
+
+ if( dev_mgr == NULL || pdriver == NULL )
+ return FALSE;
+
+ //init driver structure, no PNP table functions
+
+ pdriver->driver_desc.flags = USB_DRIVER_FLAG_IF_CAPABLE;
+ pdriver->driver_desc.vendor_id = 0x0000; // USB Vendor ID
+ pdriver->driver_desc.product_id = 0x0000; // USB Product ID.
+ pdriver->driver_desc.release_num = 0x100; // Release Number of Device
+
+ pdriver->driver_desc.config_val = 1; // Configuration Value
+ pdriver->driver_desc.if_num = 1; // Interface Number
+ pdriver->driver_desc.if_class = USB_CLASS_MASS_STORAGE; // Interface Class
+ pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
+ pdriver->driver_desc.if_protocol = 0; // Interface Protocol
+
+ pdriver->driver_desc.driver_name = "USB Mass Storage interface driver"; // Driver name for Name Registry
+ pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE;
+ pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
+ pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
+
+ pdriver->driver_ext = usb_alloc_mem( NonPagedPool, sizeof( UMSS_DRVR_EXTENSION ) );
+ pdriver->driver_ext_size = sizeof( UMSS_DRVR_EXTENSION );
+
+ RtlZeroMemory( pdriver->driver_ext, sizeof( UMSS_DRVR_EXTENSION ) );
+
+ pdrvr_ext = ( PUMSS_DRVR_EXTENSION )pdriver->driver_ext;
+ pdrvr_ext->dev_count = 0;
+ InitializeListHead( &pdrvr_ext->dev_list );
+ ExInitializeFastMutex( &pdrvr_ext->dev_list_mutex );
+
+ pdriver->disp_tbl.version = 1;
+ pdriver->disp_tbl.dev_connect = umss_if_connect;
+ pdriver->disp_tbl.dev_disconnect = umss_if_disconnect;
+ pdriver->disp_tbl.dev_stop = umss_if_stop;
+ pdriver->disp_tbl.dev_reserved = NULL;
+
+ if( ( pdrvr_ext->port_dev_obj = umss_create_port_device( dev_mgr, pdriver ) ) == NULL )
+ {
+ usb_free_mem( pdriver->driver_ext );
+ pdriver->driver_ext = NULL;
+ pdriver->driver_ext_size = 0;
+ pdriver->disp_tbl.dev_connect = NULL;
+ pdriver->disp_tbl.dev_stop = NULL;
+ pdriver->disp_tbl.dev_disconnect = NULL;
+ return FALSE;
+ }
+
+ //
+ // let's load the class driver
+ //
+
+ umss_load_class_driver( NULL );
+
+ // umss_schedule_workitem( NULL, umss_load_class_driver, NULL, 0 );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_if_driver_init(): umss driver is initialized\n" ) );
+
+ return TRUE;
+}
+
+PCLASS_DRV_REG_INFO
+umss_get_if_driver_info(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DEV pdev,
+DEV_HANDLE if_handle
+)
+// get the driver reg information for pnp notification to class
+// driver.
+// bug??? how if the driver info is returned while the driver
+// is being unloaded.
+// So the routine must be called when usb_query_and_lock_dev is
+// called.
+{
+ PUMSS_DRVR_EXTENSION drvr_ext;
+ ULONG if_idx;
+ USE_IRQL;
+
+ if_idx = if_idx_from_handle( if_handle );
+ if( if_idx >= 4 ) // max interfaces per config supports. defined in td.h
+ return NULL;
+
+ ASSERT( pdev != NULL );
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+ return NULL;
+ }
+
+ drvr_ext = NULL;
+
+ if( pdev->usb_config->interf[ if_idx ].pif_drv )
+ drvr_ext = ( PUMSS_DRVR_EXTENSION )pdev->usb_config->interf[ if_idx ].pif_drv->driver_ext;
+ else
+ TRAP();
+
+ unlock_dev( pdev, FALSE );
+
+ if( drvr_ext == NULL )
+ {
+ return NULL;
+ }
+
+ return &drvr_ext->class_driver_info;
+}
+
+VOID
+umss_worker(
+IN PVOID reference
+)
+{
+ PUMSS_WORKER_PACKET worker_packet;
+ PUSB_DEV pdev;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_worker(): entering...\n" ) );
+ worker_packet = ( PUMSS_WORKER_PACKET )reference;
+ worker_packet->completion( worker_packet->context );
+ if( worker_packet->dev_mgr && worker_packet->pdev )
+ {
+ pdev = ( PUSB_DEV ) worker_packet->pdev;
+ usb_unlock_dev( pdev );
+ pdev = NULL;
+ }
+ usb_free_mem( worker_packet );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_worker(): exit\n" ) );
+}
+
+BOOL
+umss_schedule_workitem(
+PVOID context,
+UMSS_WORKER_ROUTINE completion,
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+)
+/*++
+Routine Description:
+
+ Wrapper for handling worker thread callbacks, it is importent to
+ lock the dev from being deleted by calling usb_query_and_lock_dev
+ and in umss_worker, call the usb_unlock_dev to release the ref
+ count. One exception is that the umss_if_disconnect call this
+ function to delete the device object that is still held by some
+ others, and deferred deletion is required.
+
+ Arguments:
+
+ Routine - Routine to be called when this work-item is processed
+ Context - Value to be passed to worker routine
+
+Return Value:
+ TRUE if work item queued
+ FALSE if work item not queued
+
+--*/
+
+{
+ BOOL ret_val = TRUE;
+ PWORK_QUEUE_ITEM workitem;
+ PUMSS_WORKER_PACKET worker_packet;
+
+ worker_packet = usb_alloc_mem( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_WORKER_PACKET ) );
+ RtlZeroMemory( worker_packet, sizeof( WORK_QUEUE_ITEM ) + sizeof( UMSS_WORKER_PACKET ) );
+
+ if ( worker_packet )
+ {
+ workitem = ( PWORK_QUEUE_ITEM )&worker_packet[ 1 ];
+ worker_packet->completion = completion;
+ worker_packet->context = context;
+
+ if( dev_mgr != NULL && dev_handle != 0 )
+ {
+ PUSB_DEV pdev;
+ // lock the device until the workitem is executed.
+ if( usb_query_and_lock_dev( dev_mgr, dev_handle, &pdev ) == STATUS_SUCCESS )
+ {
+ worker_packet->dev_mgr = dev_mgr;
+ worker_packet->pdev = pdev;
+ }
+ else
+ {
+ usb_free_mem( worker_packet );
+ return FALSE;
+ }
+ }
+ // Initialize the work-item
+ ExInitializeWorkItem(
+ workitem,
+ umss_worker,
+ worker_packet
+ );
+
+ // Schedule the work-item
+ ExQueueWorkItem(
+ workitem,
+ DelayedWorkQueue
+ );
+
+ usb_dbg_print( DBGLVL_MINIMUM,( "umss_schedule_workitem(): work-item queued\n" ) );
+ }
+ else
+ {
+ usb_dbg_print( DBGLVL_MINIMUM,("umss_schedule_workitem(): Failed to allocate work-item\n"));
+ ret_val = FALSE;
+ }
+
+ return ret_val;
+}
+
+NTSTATUS
+umss_process_srb(
+PDEVICE_OBJECT dev_obj,
+PIRP irp
+)
+{
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ PIO_STACK_LOCATION cur_stack;
+ PUMSS_DEVICE_EXTENSION pdev_ext;
+ PSCSI_REQUEST_BLOCK srb;
+
+ if( dev_obj == NULL || irp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ pdev = NULL;
+ cur_stack = IoGetCurrentIrpStackLocation( irp );
+ srb = cur_stack->Parameters.Scsi.Srb;
+
+ if( srb == NULL || srb->DataTransferLength > 65536 )
+ {
+ status = STATUS_INVALID_PARAMETER;
+ goto ERROR_OUT;
+ }
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+
+ pdev_ext = ( PUMSS_DEVICE_EXTENSION )dev_obj->DeviceExtension;
+ if( ( status = usb_query_and_lock_dev( pdev_ext->dev_mgr, pdev_ext->dev_handle, &pdev ) ) != STATUS_SUCCESS )
+ {
+ PSENSE_DATA sense_buf;
+ srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+
+ //
+ // let's build the srb status for class driver
+ //
+
+ srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ RtlZeroMemory( srb->SenseInfoBuffer, srb->SenseInfoBufferLength );
+ if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) )
+ {
+ sense_buf = ( PSENSE_DATA )srb->SenseInfoBuffer;
+ sense_buf->ErrorCode = 0x70;
+ sense_buf->Valid = 1;
+ sense_buf->SenseKey = SCSI_SENSE_NOT_READY;
+ sense_buf->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ sense_buf->AdditionalSenseLength = 10;
+ }
+ goto ERROR_OUT;
+ }
+
+ switch( srb->Function )
+ {
+ case SRB_FUNCTION_EXECUTE_SCSI:
+ {
+ IO_PACKET io_packet;
+ RtlZeroMemory( &io_packet, sizeof( io_packet ) );
+
+ io_packet.flags |= IOP_FLAG_SRB_TRANSFER;
+ if( srb->SrbFlags & SRB_FLAGS_DATA_IN )
+ io_packet.flags |= IOP_FLAG_DIR_IN;
+ if( !( srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE ) )
+ io_packet.flags |= IOP_FLAG_REQ_SENSE;
+
+ io_packet.cdb_length = srb->CdbLength;
+ RtlCopyMemory( io_packet.cdb, srb->Cdb, sizeof( io_packet.cdb ) );
+ io_packet.lun = 0;
+
+ if( srb->SrbFlags & SRB_FLAGS_NO_DATA_TRANSFER )
+ {
+ io_packet.data_buffer = NULL;
+ io_packet.data_length = 0;
+ }
+ else
+ {
+ if( ( irp->Flags & ( IRP_READ_OPERATION | IRP_WRITE_OPERATION ) ) && !( irp->Flags & IRP_PAGING_IO ) )
+ {
+ //
+ // since these operations does not allign the buffer on page boundary
+ // and some unknown traps in window NT, we have to copy to a buffer
+ // we allocated
+ io_packet.data_buffer = usb_alloc_mem( NonPagedPool, srb->DataTransferLength );
+ if( irp->Flags & IRP_WRITE_OPERATION )
+ {
+ PULONG dest_buf, src_buf;
+ ULONG i;
+
+ dest_buf = ( PULONG )io_packet.data_buffer;
+ src_buf = ( PULONG )srb->DataBuffer;
+
+ if( src_buf && dest_buf )
+ {
+ for( i = 0; i < ( srb->DataTransferLength >> 2 ); i++ )
+ {
+ dest_buf[ i ] = src_buf[ i ];
+ }
+ }
+ }
+ }
+ else
+ io_packet.data_buffer = srb->DataBuffer;
+
+ io_packet.data_length = srb->DataTransferLength;
+ }
+
+ if( io_packet.flags & IOP_FLAG_REQ_SENSE )
+ {
+ io_packet.sense_data = srb->SenseInfoBuffer;
+ io_packet.sense_data_length = srb->SenseInfoBufferLength;
+ }
+
+ io_packet.pirp = irp;
+
+ // do some conversions
+ if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I )
+ {
+ if( umss_tsc_to_sff( &io_packet ) == FALSE )
+ {
+ status = STATUS_DEVICE_PROTOCOL_ERROR;
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): error converting to sff proto, 0x%x\n",
+ status ) );
+ srb->SrbStatus = SRB_STATUS_ERROR;
+ break;
+ }
+ }
+
+ if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_BULKONLY )
+ {
+ //
+ // currently we support only transparent scsi command set
+ //
+ if( pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SCSI_TCS || \
+ pdev_ext->pif_desc->bInterfaceSubClass == UMSS_SUBCLASS_SFF8070I )
+ status = umss_bulkonly_startio( pdev_ext, &io_packet );
+ else
+ status = STATUS_DEVICE_PROTOCOL_ERROR;
+ }
+ else if( pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CB
+ || pdev_ext->pif_desc->bInterfaceProtocol == PROTOCOL_CBI )
+ {
+ status = umss_cbi_startio( pdev_ext, &io_packet );
+ }
+ else
+ {
+ status = STATUS_DEVICE_PROTOCOL_ERROR;
+ }
+
+ if( status != STATUS_PENDING && status != STATUS_SUCCESS )
+ {
+ // error occured
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): error sending request, 0x%x\n",
+ status ) );
+ srb->SrbStatus = SRB_STATUS_ERROR;
+ }
+ break;
+ }
+ case SRB_FUNCTION_CLAIM_DEVICE:
+ {
+ srb->DataBuffer = ( PVOID )dev_obj;
+ }
+ case SRB_FUNCTION_SHUTDOWN:
+ case SRB_FUNCTION_FLUSH:
+ case SRB_FUNCTION_RESET_BUS:
+ case SRB_FUNCTION_FLUSH_QUEUE:
+ case SRB_FUNCTION_RELEASE_QUEUE:
+ case SRB_FUNCTION_RELEASE_DEVICE:
+ default:
+ {
+ // for usb flash disk, they are luxurious
+
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "umss_process_srb(): current srb->Function=0x%x\n",
+ srb->Function ) );
+
+ status = STATUS_SUCCESS;
+ srb->SrbStatus = SRB_STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ usb_unlock_dev( pdev );
+ pdev = NULL;
+
+ERROR_OUT:
+
+ irp->IoStatus.Status = status;
+ if( status != STATUS_PENDING )
+ {
+ IoStartNextPacket( dev_obj, FALSE );
+ IoCompleteRequest( irp, IO_NO_INCREMENT );
+ }
+ //
+ // UMSS_COMPLETE_START_IO( dev_obj, status, irp );
+ //
+ return status;
+}
+
+BOOL
+umss_tsc_to_sff(
+PIO_PACKET io_packet
+)
+{
+ if( io_packet == NULL )
+ return FALSE;
+
+ io_packet->cdb_length = 12;
+ if( io_packet->cdb[ 0 ] == SCSIOP_MODE_SENSE )
+ {
+ io_packet->cdb[ 0 ] = 0x5a; // mode sense( 10 )
+ io_packet->cdb[ 8 ] = io_packet->cdb[ 4 ];
+ io_packet->cdb[ 4 ] = 0;
+ if( io_packet->cdb[ 8 ] < 8 )
+ io_packet->cdb[ 8 ] = 8;
+
+ io_packet->data_length = 8;
+ return TRUE;
+ }
+ if( io_packet->cdb[ 0 ] == SCSIOP_REASSIGN_BLOCKS || \
+ io_packet->cdb[ 0 ] == SCSIOP_RESERVE_UNIT || \
+ io_packet->cdb[ 0 ] == SCSIOP_RELEASE_UNIT )
+ return FALSE;
+
+ return TRUE;
+}
+VOID
+umss_fix_sff_result(
+PIO_PACKET io_packet,
+SCSI_REQUEST_BLOCK *srb )
+{
+ PBYTE buf;
+ if( io_packet->cdb[ 0 ] != 0x5a )
+ return;
+ // the following is not right since it has to be 0x3f, return all pages
+ // if( io_packet->cdb[ 2 ] != 0 )
+ // return;
+ srb->DataTransferLength = 4;
+ buf = io_packet->data_buffer;
+ // convert the mode param to scsi II
+ buf[ 0 ] = buf[ 1 ];
+ buf[ 1 ] = buf[ 2 ];
+ buf[ 2 ] = buf[ 3 ];
+ buf[ 3 ] = 0;
+ return;
+}
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/umss.h b/reactos/drivers/usb/nt4compat/usbdriver/umss.h
new file mode 100644
index 00000000000..1adda3cfee7
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/umss.h
@@ -0,0 +1,353 @@
+#ifndef __UMSS_H__
+#define __UMSS_H__
+
+#include "td.h"
+#include "hub.h"
+
+#define MAX_BULK_TRANSFER_LENGTH 0x100000
+
+#define PROTOCOL_CBI 0x00
+#define PROTOCOL_CB 0x01
+#define PROTOCOL_BULKONLY 0x50
+
+#define PROTOCOL_UNDEFINED 0xFF // Not in spec
+
+#define UMSS_SUBCLASS_RBC 0x01
+#define UMSS_SUBCLASS_SFF8020I 0X02
+#define UMSS_SUBCLASS_QIC157 0x03
+#define UMSS_SUBCLASS_UFI 0x04
+#define UMSS_SUBCLASS_SFF8070I 0x05
+#define UMSS_SUBCLASS_SCSI_TCS 0x06
+
+#define ACCEPT_DEVICE_SPECIFIC_COMMAND 0
+
+#define BULK_ONLY_MASS_STORAGE_RESET 0xFF
+#define BULK_ONLY_GET_MAX_LUN 0xFE
+
+#define CBW_SIGNATURE 0x43425355L
+#define CSW_SIGNATURE 0x53425355L
+#define CSW_OLYMPUS_SIGNATURE 0x55425355L
+
+#define CSW_STATUS_PASSED 0x00
+#define CSW_STATUS_FAILED 0x01
+#define CSW_STATUS_PHASE_ERROR 0x02
+
+#define IOCTL_UMSS_SUBMIT_CDB CTL_CODE( FILE_USB_DEV_TYPE, 4200, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// for request with no other input and output
+// input buffer is a _USER_IO_PACKET and input_buffer_length is length of the _USER_IO_PACKET
+// output_buffer is NULL, and output_buffer_length is zero
+
+#define IOCTL_UMSS_SUBMIT_CDB_IN CTL_CODE( FILE_USB_DEV_TYPE, 4201, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
+// for request to read in data
+// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET
+// output_buffer is a buffer to receive the data from dev, and
+// output_buffer_length is the size of the buffer
+
+#define IOCTL_UMSS_SUBMIT_CDB_OUT CTL_CODE( FILE_USB_DEV_TYPE, 4202, METHOD_OUT_DIRECT, FILE_ANY_ACCESS )
+// for request to write data to device
+// input_buffer is a _USER_IO_PACKET and input_buffer_length is length to the _USER_IO_PACKET
+// output_buffer is data to send to the device, and
+// output_buffer_length is the size of the buffer
+
+#define IOCTL_REGISTER_DRIVER CTL_CODE( FILE_USB_DEV_TYPE, 4203, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// input_buffer is a CLASS_DRV_REG_INFO, and input_buffer_length is equal to or greater than
+// sizeof( CLASS_DRV_REG_INFO ); the output_buffer is null and no output_buffer_length,
+// only the following fields in urb can be accessed, others must be zeroed.
+
+#define IOCTL_REVOKE_DRIVER CTL_CODE( FILE_USB_DEV_TYPE, 4204, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// tell the umss driver to clear the information in the drivers registry
+// no other parameters
+
+#define IOCTL_UMSS_SUBMIT_SRB CTL_CODE( FILE_USB_DEV_TYPE, 4205, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// irpStack->Parameters.Scsi.Srb points to an Srb structure and all the data buffer and buffer
+// size are stored in the srb
+
+
+#define IOCTL_UMSS_SET_FDO CTL_CODE( FILE_USB_DEV_TYPE, 4206, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// input_buffer is a pointer to PDEVICE_OBJECT, and input_buffer_length should be
+// no less than sizeof( PDEVICE_OBJECT )
+// output buffer is NULL, and output_buffer_length is zero
+// if the deivce is accessable, the fdo is set, else, the fdo is not set and return
+// STATUS_DEVICE_DOES_NOT_EXIST
+
+#define SFF_FORMAT_UNIT 0x04
+#define SFF_INQUIRY 0x12
+#define SFF_MODE_SELECT 0x55
+#define SFF_MODE_SENSE 0x5a
+#define SFF_ALLOW_REMOVE 0x1e
+#define SFF_READ10 0x28
+#define SFF_READ12 0xa8
+#define SFF_READ_CAPACITY 0x25
+#define SFF_REQUEST_SENSEE 0x03
+#define SFF_SEEK 0x2b
+#define SFF_START_STOP 0x1b
+#define SFF_TUR 0x00
+#define SFF_VERIFY 0x2f
+#define SFF_WRITE10 0x2a
+#define SFF_WRITE12 0xaa
+#define SFF_READ_FMT_CAPACITY 0x23
+#define SFF_WRITE_VERIFY 0x2e
+
+#define MAX_CDB_LENGTH 0x10
+
+typedef struct _USER_IO_PACKET
+{
+ UCHAR sub_class;
+ UCHAR lun;
+ UCHAR cdb_length;
+ UCHAR cdb[ MAX_CDB_LENGTH ];
+
+} USER_IO_PACKET, *PUSER_IO_PACKET;
+
+//flags for IO_PACKET::flags
+#define IOP_FLAG_REQ_SENSE 0x80000000 // sense data would be fetched if error occurs
+#define IOP_FLAG_SRB_TRANSFER 0x40000000 // current tranfer is initiated by an srb request, the srb is held by the irp
+#define IOP_FLAG_SCSI_CTRL_TRANSFER 0x20000000 // current transfer is initiated by an scsi ioctrl request
+
+#define IOP_FLAG_DIR_IN USB_DIR_IN
+#define IOP_FLAG_STAGE_MASK 0x03
+#define IOP_FLAG_STAGE_NORMAL 0x00
+#define IOP_FLAG_STAGE_SENSE 0x01
+
+typedef struct _IO_PACKET
+{
+ ULONG flags;
+ UCHAR cdb_length;
+ UCHAR cdb[ MAX_CDB_LENGTH ];
+ UCHAR lun;
+ PVOID data_buffer;
+ ULONG data_length;
+ PVOID sense_data;
+ ULONG sense_data_length;
+ PIRP pirp;
+
+} IO_PACKET, *PIO_PACKET;
+
+#pragma pack( 1 )
+
+typedef struct _COMMAND_BLOCK_WRAPPER
+{
+ ULONG dCBWSignature;
+ ULONG dCBWTag;
+ ULONG dCBWDataTransferLength;
+ UCHAR bmCBWFlags;
+ UCHAR bCBWLun;
+ UCHAR bCBWLength;
+ UCHAR CBWCB[ MAX_CDB_LENGTH ];
+
+} COMMAND_BLOCK_WRAPPER, *PCOMMAND_BLOCK_WRAPPER;
+
+typedef struct _COMMAND_STATUS_WRAPPER
+{
+ ULONG dCSWSignature;
+ ULONG dCSWTag;
+ ULONG dCSWDataResidue;
+ UCHAR bCSWStatus;
+
+} COMMAND_STATUS_WRAPPER, *PCOMMAND_STATUS_WRAPPER;
+
+
+typedef struct _INTERRUPT_DATA_BLOCK
+{
+ UCHAR bType;
+ UCHAR bValue;
+
+} INTERRUPT_DATA_BLOCK, *PINTERRUPT_DATA_BLOCK;
+
+#pragma pack()
+
+#define UMSS_PNPMSG_STOP 0x01
+#define UMSS_PNPMSG_DISCONNECT 0x02
+
+typedef NTSTATUS ( *PCLASS_DRVR_PNP_DISP )( PDEVICE_OBJECT pdo, ULONG ctrl_code, PVOID context );
+ // pdo is the device object umss created
+
+typedef PDEVICE_OBJECT ( *PCLASS_DRIVER_ADD_DEV )( PDRIVER_OBJECT fdo_drvr, PDEVICE_OBJECT pdo );
+ // if the return value is not zero, it is a pointer to the
+ // fdo sitting over the pdo of this driver. if it is null,
+ // the add_device failed, and initialization process should
+ // stall.
+
+typedef struct _CLASS_DRV_REGISTRY_INFO
+{
+ // class driver will pass this structure to umss port
+ // driver after loaded
+ PDRIVER_OBJECT fdo_driver;
+ PCLASS_DRIVER_ADD_DEV add_device;
+ PCLASS_DRVR_PNP_DISP pnp_dispatch;
+
+} CLASS_DRV_REG_INFO, *PCLASS_DRV_REG_INFO;
+
+typedef struct _UMSS_PORT_DEVICE_EXTENSION
+{
+ // this structure is the device extension for port dev_obj
+ // it is used to has class driver pass CLASS_DRV_REG_INFO
+ // to our umss driver.
+ DEVEXT_HEADER dev_ext_hdr;
+ PUSB_DRIVER pdriver;
+
+} UMSS_PORT_DEV_EXT, *PUMSS_PORT_DEV_EXT;
+
+typedef struct _UMSS_DRVR_EXTENSION
+{
+ LIST_HEAD dev_list;
+ FAST_MUTEX dev_list_mutex;
+ UCHAR dev_count;
+ CLASS_DRV_REG_INFO class_driver_info;
+ PDEVICE_OBJECT port_dev_obj; // we use this obj as a connection point for class driver, its name usbPort0
+
+} UMSS_DRVR_EXTENSION, *PUMSS_DRVR_EXTENSION;
+
+#define UMSS_DEV_FLAG_IF_DEV 0x01
+#define UMSS_DEV_FLAG_OLYMPUS_DEV 0x02
+
+#define UMSS_OLYMPUS_VENDOR_ID 0x07b4
+
+typedef struct _UMSS_DEVICE_EXTENSION
+{
+ //this structure is the device extension for dev_obj
+ //created for the device.
+ DEVEXT_HEADER dev_ext_hdr;
+
+ ULONG flags;
+ LIST_ENTRY dev_obj_link; // this link is used by the driver object to track the existing dev_objs
+
+ PDEVICE_OBJECT pdo; // this is the pdo
+ PDEVICE_OBJECT fdo; // driver object for the dev_obj
+
+ DEV_HANDLE dev_handle; // handle to the usb_dev under
+
+ PUCHAR desc_buf;
+ UCHAR umss_dev_id; // used to build symbolic link
+
+ PUSB_INTERFACE_DESC pif_desc;
+ PUSB_ENDPOINT_DESC pout_endp_desc, pin_endp_desc, pint_endp_desc;
+ UCHAR if_idx, out_endp_idx, in_endp_idx, int_endp_idx;
+
+ struct _USB_DEV_MANAGER *dev_mgr;
+
+ //working data
+ COMMAND_BLOCK_WRAPPER cbw;
+ union
+ {
+ INTERRUPT_DATA_BLOCK idb;
+ COMMAND_STATUS_WRAPPER csw;
+ };
+
+ KEVENT sync_event; //for umss_sync_submit_urb
+ KSPIN_LOCK dev_lock;
+ IO_PACKET io_packet;
+ BOOL retry;
+
+ PUSB_DRIVER pdriver; //used by umss_delete_device
+ NTSTATUS reset_pipe_status;
+} UMSS_DEVICE_EXTENSION, *PUMSS_DEVICE_EXTENSION;
+
+// for device creation workitem
+typedef struct _UMSS_CREATE_DATA
+{
+ DEV_HANDLE dev_handle;
+ PUCHAR desc_buf;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_DRIVER pdriver;
+
+} UMSS_CREATE_DATA, *PUMSS_CREATE_DATA;
+
+// for reset pipe item
+//typedef void ( _stdcall *COMPLETION_HANDLER )( PVOID );
+typedef void ( *UMSS_WORKER_ROUTINE )( PVOID );
+
+typedef struct _UMSS_WORKER_PACKET
+{
+ UMSS_WORKER_ROUTINE completion;
+ PVOID context;
+ PUSB_DEV_MANAGER dev_mgr;
+ PVOID pdev;
+
+} UMSS_WORKER_PACKET, *PUMSS_WORKER_PACKET;
+
+BOOL
+umss_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+umss_if_driver_init(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+BOOL
+umss_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+#define umss_if_driver_destroy umss_driver_destroy
+
+BOOL
+umss_if_driver_destroy(
+PUSB_DEV_MANAGER dev_mgr,
+PUSB_DRIVER pdriver
+);
+
+VOID
+umss_complete_request(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+NTSTATUS status
+);
+
+NTSTATUS
+umss_reset_pipe(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+DEV_HANDLE endp_handle
+);
+
+PVOID
+umss_get_buffer(
+PUMSS_DEVICE_EXTENSION pdev_ext,
+ULONG* buf_length
+);
+
+NTSTATUS
+umss_bulk_transfer(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN UCHAR trans_dir,
+IN PVOID buf,
+IN ULONG buf_length,
+IN PURBCOMPLETION completion
+);
+
+BOOL
+umss_schedule_workitem(
+PVOID context,
+UMSS_WORKER_ROUTINE completion,
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle
+);
+
+NTSTATUS
+umss_bulkonly_startio(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN PIO_PACKET io_packet
+);
+
+NTSTATUS
+umss_cbi_startio(
+IN PUMSS_DEVICE_EXTENSION pdev_ext,
+IN PIO_PACKET io_packet
+);
+
+#define UMSS_FORGE_GOOD_SENSE( sense_BUF ) \
+{\
+ int i;\
+ PUCHAR buf = ( PUCHAR )( sense_BUF ); \
+ for( i = 0; i < 18; i++)\
+ {\
+ buf[i] = 0;\
+ }\
+ buf[7] = 10;\
+}
+
+#endif
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usb.c b/reactos/drivers/usb/nt4compat/usbdriver/usb.c
new file mode 100644
index 00000000000..06e97a0deda
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/usb.c
@@ -0,0 +1,1518 @@
+/**
+ * usb.c - USB driver stack project for Windows NT 4.0
+ *
+ * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program (in the main directory of the distribution, the file
+ * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ntddk.h"
+#include "usb.h"
+#include "hub.h"
+#include "debug.h"
+
+LONG g_alloc_cnt = 0;
+ULONG cpu_clock_freq = 0;
+
+NTSTATUS
+usb_get_descriptor(
+PUSB_DEV pdev,
+PURB purb
+);
+
+VOID
+usb_set_interface_completion(
+PURB purb,
+PVOID context
+);
+
+NTSTATUS
+usb_set_interface(
+PURB purb
+);
+
+PVOID
+usb_alloc_mem(
+POOL_TYPE pool_type,
+LONG size
+)
+{
+ PVOID ret;
+ g_alloc_cnt++;
+ ret = ExAllocatePool( pool_type, size );
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_alloc_mem(): alloced=0x%x\n", g_alloc_cnt ) );
+ return ret;
+}
+
+VOID
+usb_free_mem(
+PVOID pbuf
+)
+{
+ g_alloc_cnt--;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_free_mem(): alloced=0x%x\n", g_alloc_cnt ) );
+ ExFreePool( pbuf);
+}
+
+VOID
+usb_config_dev_completion(
+PURB purb,
+PVOID context
+);
+
+LONG
+usb_calc_bus_time(
+LONG speed,
+LONG input_dir,
+LONG is_iso,
+LONG byte_count
+)
+//shamelessly pasted from linux's usb.c
+{
+ LONG tmp;
+
+ switch( speed & 0x3 ) /* no isoc. here */
+ {
+ case USB_SPEED_LOW:
+ {
+ if (input_dir)
+ {
+ tmp = (67667L * (31L + 10L * bit_time (byte_count))) / 1000L;
+ return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ else
+ {
+ tmp = (66700L * (31L + 10L * bit_time (byte_count))) / 1000L;
+ return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ break;
+ }
+ /* for full-speed: */
+ case USB_SPEED_FULL:
+ {
+ if (!is_iso) /* Input or Output */
+ {
+ tmp = (8354L * (31L + 10L * bit_time (byte_count))) / 1000L;
+ return (9107L + BW_HOST_DELAY + tmp);
+ } /* end not Isoc */
+
+ /* for isoc: */
+
+ tmp = (8354L * (31L + 10L * bit_time (byte_count))) / 1000L;
+ return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+ }
+ case USB_SPEED_HIGH:
+ {
+ if( !is_iso )
+ {
+ tmp = ( 999 + 926520 + 2083 * ( ( LONG )( ( 19 + 7 * 8 * byte_count ) / 6 ) ) ) / 1000;
+ }
+ else
+ {
+ tmp = ( 999 + 633232 + 2083 * ( ( LONG )( ( 19 + 7 * 8 * byte_count ) / 6 ) ) ) / 1000;
+ }
+ return tmp + USB2_HOST_DELAY;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ return 125001;
+}
+
+NTSTATUS
+usb_query_and_lock_dev(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE dev_handle,
+PUSB_DEV* ppdev
+)
+//
+// if the dev is not in the list, return value is not success and the pointer is nulled
+// if the dev is in the list but zomb, return value is error code and the pointer is the dev( no ref_count guarded )
+// if the dev is alive and in the list, return is success and the pointer is the dev.
+// one must be aware of what his doing before he uses the ppdev
+//
+{
+ int i;
+ PLIST_ENTRY pthis, pnext;
+ PUSB_DEV pdev;
+ BOOL valid_dev;
+
+ USE_IRQL;
+
+ *ppdev = NULL;
+
+ if( dev_mgr == NULL || dev_handle == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ i = dev_id_from_handle( dev_handle );
+
+ KeAcquireSpinLock( &dev_mgr->dev_list_lock, &old_irql );
+ ListFirst( &dev_mgr->dev_list, pthis );
+
+ while( pthis )
+ {
+ pdev = ( PUSB_DEV ) pthis;
+ if( pdev->dev_id != ( ULONG )i )
+ {
+ ListNext( &dev_mgr->dev_list, pthis, pnext );
+ pthis = pnext;
+ continue;
+ }
+ else
+ break;
+ }
+ if( pthis == NULL )
+ {
+ //no such device
+ KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ valid_dev = TRUE;
+
+ lock_dev( pdev, TRUE );
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ valid_dev = FALSE;
+ }
+ else
+ pdev->ref_count ++; //guard the dev by increasing the ref count
+
+ unlock_dev( pdev, TRUE );
+
+ KeReleaseSpinLock( &dev_mgr->dev_list_lock, old_irql );
+
+ *ppdev = pdev;
+
+ if( !valid_dev )
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+usb_unlock_dev(
+PUSB_DEV dev
+)
+{
+ USE_IRQL;
+
+ if( dev == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( dev, FALSE );
+ dev->ref_count --;
+ if( dev->ref_count < 0 )
+ dev->ref_count = 0;
+ unlock_dev( dev, FALSE );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+usb_reset_pipe_ex(
+PUSB_DEV_MANAGER dev_mgr,
+DEV_HANDLE endp_handle, //endp handle to reset
+PURBCOMPLETION reset_completion, //note: this reset completion has no right to delete the urb, that is only for reference
+PVOID param
+)
+{
+ NTSTATUS status;
+ PUSB_DEV pdev;
+ LONG if_idx, endp_idx;
+ PUSB_ENDPOINT pendp;
+ USE_IRQL;
+
+ if( dev_mgr == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ status = usb_query_and_lock_dev( dev_mgr, ( endp_handle & 0xffff0000 ), &pdev );
+ if( status != STATUS_SUCCESS )
+ return STATUS_UNSUCCESSFUL;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT;
+ }
+
+ if_idx = if_idx_from_handle( endp_handle );
+ endp_idx = endp_idx_from_handle( endp_handle );
+
+ if( default_endp_handle( endp_handle ) )
+ {
+ status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT;
+ }
+
+ if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ goto LBL_OUT;
+ }
+
+ pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ];
+ unlock_dev( pdev, FALSE )
+ status = usb_reset_pipe( pdev, pendp, reset_completion, param );
+ usb_unlock_dev( pdev );
+ return status;
+
+LBL_OUT:
+ unlock_dev( pdev, FALSE );
+ usb_unlock_dev( pdev );
+
+ return status;
+}
+
+NTSTATUS
+usb_reset_pipe(
+PUSB_DEV pdev,
+PUSB_ENDPOINT pendp,
+PURBCOMPLETION client_reset_pipe_completion,
+PVOID param
+)
+// caller must guarantee the pdev exist before the routine exit
+{
+
+ PHCD hcd;
+ PURB purb;
+ BYTE endp_addr;
+ NTSTATUS status;
+ DEV_HANDLE dev_handle;
+
+ USE_IRQL;
+
+ if( pdev == NULL || pendp == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ hcd = pdev->hcd;
+ endp_addr = pendp->pusb_endp_desc->bEndpointAddress;
+ dev_handle = usb_make_handle( pdev->dev_id, 0, 0 );
+ unlock_dev( pdev, FALSE );
+
+ purb = ( PURB )usb_alloc_mem( NonPagedPool, sizeof( URB ) + sizeof( PIRP ) );
+
+ if( purb == NULL )
+ return STATUS_NO_MEMORY;
+
+ UsbBuildResetPipeRequest( purb,
+ dev_handle,
+ endp_addr,
+ usb_reset_pipe_completion,
+ pendp,
+ ( LONG )client_reset_pipe_completion );
+
+ *( ( PULONG )&purb[ 1 ] ) = ( ULONG )param;
+
+ if( ( status = hcd->hcd_submit_urb( hcd, pdev, &pdev->default_endp, purb ) ) != STATUS_PENDING )
+ {
+ usb_free_mem( purb );
+ purb = NULL;
+ }
+ return status;
+}
+
+VOID
+usb_reset_pipe_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+
+ USE_IRQL;
+
+ if( purb == NULL || context == NULL )
+ return;
+
+ pdev = purb->pdev;
+ pendp = ( PUSB_ENDPOINT )context;
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ goto LBL_OUT;
+ }
+
+ if( usb_error( purb->status ) )
+ {
+ goto LBL_OUT;
+ }
+ //clear stall
+ pendp->flags &= ~USB_ENDP_FLAG_STAT_MASK;
+
+ //reset toggle endp_type
+ if( ( pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) == USB_ENDPOINT_XFER_BULK || \
+ ( pendp->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) == USB_ENDPOINT_XFER_INT )
+ {
+ pendp->flags &= ~USB_ENDP_FLAG_DATATOGGLE;
+ }
+
+LBL_OUT:
+ unlock_dev( pdev, TRUE );
+
+ if( purb->reference )
+ ( ( PURBCOMPLETION )purb->reference )( purb, ( PVOID )( *( ( PULONG )&purb[ 1 ] ) ) );
+
+ usb_free_mem( purb );
+ purb = NULL;
+ return;
+
+}
+
+void
+usb_reset_pipe_from_dispatch_completion(
+PURB purb,
+PVOID param
+)
+{
+
+ PURB pclient_urb;
+ if( purb == NULL || param == NULL )
+ TRAP();
+ pclient_urb = ( PURB )param;
+ pclient_urb->status = purb->status;
+
+ if( pclient_urb->completion )
+ {
+ pclient_urb->completion( pclient_urb, pclient_urb->context );
+ }
+ // the urb can not be freed here because it is owned by the reset
+ // pipe completion
+ return;
+}
+
+BOOL
+is_header_match(
+PBYTE pbuf,
+ULONG type
+) //used to check descriptor validity
+{
+ BOOL ret;
+ PUSB_DESC_HEADER phdr;
+ phdr = ( PUSB_DESC_HEADER )pbuf;
+
+ switch( type )
+ {
+ case USB_DT_DEVICE:
+ {
+ ret = ( phdr->bLength == sizeof( USB_DEVICE_DESC )
+ && phdr->bDescriptorType == USB_DT_DEVICE );
+ break;
+ }
+ case USB_DT_CONFIG:
+ {
+ ret = ( phdr->bLength == sizeof( USB_CONFIGURATION_DESC )
+ && phdr->bDescriptorType == USB_DT_CONFIG );
+ break;
+ }
+ case USB_DT_INTERFACE:
+ {
+ ret = ( phdr->bLength == sizeof( USB_INTERFACE_DESC )
+ && phdr->bDescriptorType == USB_DT_INTERFACE );
+ break;
+ }
+ case USB_DT_ENDPOINT:
+ {
+ ret = ( phdr->bLength == sizeof( USB_ENDPOINT_DESC )
+ && phdr->bDescriptorType == USB_DT_ENDPOINT );
+ break;
+ }
+ default:
+ ret = FALSE;
+ }
+ return ret;
+}
+
+BOOL
+usb_skip_endp_desc(
+PBYTE* pbUF,
+LONG n
+)
+{
+ if( is_header_match( *pbUF, USB_DT_ENDPOINT ) )
+ {
+ ( *pbUF ) += sizeof( USB_ENDPOINT_DESC ) * n ;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL
+usb_skip_if_desc(
+PBYTE* pBUF
+)
+{
+ BOOL ret;
+ PUSB_INTERFACE_DESC pif_desc = ( PUSB_INTERFACE_DESC )*pBUF;
+ LONG endp_count;
+ ret = is_header_match( ( PBYTE )*pBUF, USB_DT_INTERFACE );
+ if( ret == TRUE )
+ {
+ endp_count = pif_desc->bNumEndpoints;
+ if( endp_count < MAX_ENDPS_PER_IF )
+ {
+ pif_desc ++;
+ ret = usb_skip_endp_desc( ( PBYTE* )&pif_desc, endp_count );
+ if( ret )
+ *( pBUF ) = ( PBYTE )pif_desc;
+ }
+ else
+ ret = FALSE;
+ }
+ return ret;
+}
+
+BOOL
+usb_skip_if_and_altif(
+PUCHAR* pdesc_BUF
+)
+{
+ BOOL ret;
+ PUSB_INTERFACE_DESC pif_desc1 = ( PUSB_INTERFACE_DESC )*pdesc_BUF;
+ ret = is_header_match( *pdesc_BUF, USB_DT_INTERFACE );
+ if( ret == TRUE )
+ {
+ if( pif_desc1->bAlternateSetting == 0 )
+ ret = usb_skip_if_desc( ( PUCHAR* )&pif_desc1 );
+ else
+ //no default interface
+ ret = FALSE;
+
+ while( ret && pif_desc1->bAlternateSetting != 0 )
+ ret = usb_skip_if_desc( ( PUCHAR* )&pif_desc1 );
+ }
+ if( ret )
+ *pdesc_BUF = ( PUCHAR )pif_desc1;
+
+ return ret;
+}
+BOOL
+usb_skip_one_config(
+PUCHAR* pconfig_desc_BUF
+)
+{
+ LONG if_count, i;
+ BOOL ret;
+ PUSB_CONFIGURATION_DESC pcfg_DESC = ( PUSB_CONFIGURATION_DESC )*pconfig_desc_BUF;
+ PUSB_INTERFACE_DESC pif_desc2 = ( PUSB_INTERFACE_DESC )&pcfg_DESC[ 1 ];
+
+ ret = is_header_match( ( PUCHAR )pcfg_DESC, USB_DT_CONFIG );
+ if( ret )
+ *pconfig_desc_BUF = &( ( BYTE* )pcfg_DESC )[ pcfg_DESC->wTotalLength ];
+ return ret;
+
+ ret = is_header_match( ( PUCHAR )pcfg_DESC, USB_DT_CONFIG )
+ && is_header_match( ( PUCHAR ) pif_desc2, USB_DT_INTERFACE );
+
+ if( ret )
+ {
+ if_count = pcfg_DESC->bNumInterfaces;
+ if( if_count < MAX_INTERFACES_PER_CONFIG )
+ {
+ for( i = 0; i < if_count; i++ )
+ {
+ ret = usb_skip_if_and_altif( ( PUCHAR* ) &pif_desc2 );
+ if( ret == FALSE )
+ break;
+ }
+ if( ret )
+ *pconfig_desc_BUF = ( PUCHAR )pif_desc2;
+ }
+ }
+ return ret;
+}
+
+PUSB_CONFIGURATION_DESC
+usb_find_config_desc_by_idx(
+PUCHAR pbuf,
+LONG idx,
+LONG cfg_count
+)
+{
+ LONG i;
+ BOOL ret;
+ PUSB_CONFIGURATION_DESC pcfg_desc = ( PUSB_CONFIGURATION_DESC )pbuf;
+ if( pcfg_desc == NULL )
+ return NULL;
+
+ if( cfg_count > MAX_CONFIGS_PER_DEV )
+ return NULL;
+
+ if( idx > cfg_count )
+ return NULL;
+
+ if( idx == 0 )
+ return pcfg_desc;
+
+ for( i = 0; i < idx - 1; i++ )
+ {
+ ret = usb_skip_one_config( ( PBYTE* )&pcfg_desc );
+ if( ret == FALSE )
+ return NULL;
+ }
+ return pcfg_desc;
+}
+
+PUSB_CONFIGURATION_DESC
+usb_find_config_desc_by_val(
+PBYTE pbuf,
+LONG val,
+LONG cfg_count
+)
+{
+ LONG i;
+ BOOL ret;
+ PUSB_CONFIGURATION_DESC pcfg_desc = ( PUSB_CONFIGURATION_DESC )pbuf;
+ if( pcfg_desc == NULL )
+ return NULL;
+
+ if( cfg_count > MAX_CONFIGS_PER_DEV )
+ return NULL;
+
+ for( i = 0; i < cfg_count; i++ )
+ {
+ if( pcfg_desc->bConfigurationValue == val )
+ return pcfg_desc;
+
+ ret = usb_skip_one_config( ( PBYTE* )&pcfg_desc );
+ if( ret == FALSE )
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#define if_from_handle( handle ) ( ( handle & 0xff00 ) >> 8 )
+
+NTSTATUS
+usb_submit_config_urb(
+PURB purb
+)
+{
+ PUSB_DEV pdev;
+ PUSB_DEV_MANAGER dev_mgr;
+ PUSB_ENDPOINT pendp;
+ PURB purb1;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ NTSTATUS status;
+ PHCD hcd;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+
+ lock_dev( pdev, FALSE );
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto LBL_OUT;
+ }
+
+ if( dev_state( pdev ) == USB_DEV_STATE_FIRST_CONFIG
+ || dev_state( pdev ) == USB_DEV_STATE_RECONFIG )
+ {
+ //outstanding request of set configuration exists in process
+ status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT;
+ }
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ if( dev_state( pdev ) == USB_DEV_STATE_CONFIGURED
+ && pdev->usb_config->pusb_config_desc->bConfigurationValue == ( BYTE )psetup->wValue )
+ {
+ //already the current config
+ status = STATUS_SUCCESS;
+ goto LBL_OUT;
+ }
+
+
+ if( dev_state( pdev ) == USB_DEV_STATE_CONFIGURED )
+ {
+ // not support re-configuration yet
+ status = STATUS_NOT_SUPPORTED;
+ goto LBL_OUT;
+ }
+
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ purb1 = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb1 == NULL )
+ {
+ status = STATUS_NO_MEMORY;
+ goto LBL_OUT;
+ }
+
+ UsbBuildSelectConfigurationRequest( purb1,
+ usb_make_handle( pdev->dev_id, 0, 0 ) | 0xffff,
+ psetup->wValue,
+ usb_config_dev_completion,
+ 0,
+ ( ( ULONG )purb ) );
+ purb1->pdev = pdev;
+ purb1->pendp = pendp;
+
+ //change the dev state
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_FIRST_CONFIG;
+
+ unlock_dev( pdev, FALSE );
+
+ status = hcd->hcd_submit_urb( hcd, pdev, pendp, purb1 );
+ if( status != STATUS_PENDING )
+ {
+ usb_free_mem( purb1 );
+ purb1 = NULL;
+ }
+ return status;
+
+ LBL_OUT:
+ unlock_dev( pdev, FALSE );
+ return status;
+}
+
+
+NTSTATUS
+usb_submit_urb(
+PUSB_DEV_MANAGER dev_mgr,
+PURB purb
+)
+{
+ NTSTATUS status;
+ PUSB_DEV pdev, parent_dev;
+ LONG if_idx, endp_idx;
+ DEV_HANDLE endp_handle, parent_dev_handle;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_ENDPOINT pendp;
+
+ PHCD hcd;
+ USE_IRQL;
+
+ if( purb == NULL || dev_mgr == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ endp_handle = purb->endp_handle;
+
+ if( endp_handle == 0 )
+ return STATUS_INVALID_PARAMETER;
+
+ status = usb_query_and_lock_dev( dev_mgr, endp_handle, &pdev );
+ if( status != STATUS_SUCCESS )
+ {
+ return status;
+ }
+
+ if_idx = if_idx_from_handle( endp_handle );
+ endp_idx = endp_idx_from_handle( endp_handle );
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ goto LBL_OUT;
+ }
+
+ if( dev_state( pdev ) < USB_DEV_STATE_ADDRESSED )
+ {
+ unlock_dev( pdev, FALSE );
+ status = STATUS_DEVICE_NOT_READY;
+ goto LBL_OUT;
+ }
+
+ dev_mgr = dev_mgr_from_dev( pdev );
+ hcd = pdev->hcd;
+
+ if( default_endp_handle( endp_handle ) )
+ {
+ //default endp
+ pendp = &pdev->default_endp;
+ }
+ else if( if_idx >= MAX_INTERFACES_PER_CONFIG || endp_idx >= MAX_ENDPS_PER_IF )
+ {
+ status = STATUS_INVALID_PARAMETER;
+ unlock_dev( pdev, FALSE );
+ goto LBL_OUT;
+ }
+ else
+ {
+ if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED )
+ {
+ status = STATUS_DEVICE_NOT_READY;
+ unlock_dev( pdev, FALSE );
+ goto LBL_OUT;
+ }
+ pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ];
+
+ }
+
+ purb->pdev = pdev;
+ purb->pendp = pendp;
+
+ //for default endpoint we have some special process
+ if( default_endp_handle( endp_handle ) )
+ {
+ psetup = ( PUSB_CTRL_SETUP_PACKET ) purb->setup_packet;
+ if( psetup->bmRequestType == 0
+ && psetup->bRequest == USB_REQ_SET_CONFIGURATION )
+ {
+ unlock_dev( pdev, FALSE );
+ status = usb_submit_config_urb( purb );
+ goto LBL_OUT;
+ }
+ else if( psetup->bmRequestType == 1
+ && psetup->bRequest == USB_REQ_SET_INTERFACE )
+ {
+ unlock_dev( pdev, FALSE );
+ // status = STATUS_NOT_SUPPORTED;
+ status = usb_set_interface( purb );
+ goto LBL_OUT;
+ }
+ else if( psetup->bmRequestType == 0x80
+ && psetup->bRequest == USB_REQ_GET_DESCRIPTOR )
+ {
+ if( ( psetup->wValue >> 8 ) == USB_DT_CONFIG
+ || ( psetup->wValue >> 8 ) == USB_DT_DEVICE )
+ {
+ unlock_dev( pdev, FALSE );
+ status = usb_get_descriptor( pdev, purb );
+ goto LBL_OUT;
+
+ //get the descriptor directly
+ //status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+ //goto LBL_OUT;
+ }
+ }
+ else if( psetup->bmRequestType == 0x02
+ && psetup->bRequest == USB_REQ_CLEAR_FEATURE
+ && psetup->wValue == 0 ) //reset pipe
+ {
+ ULONG endp_addr, endp_handle_to_reset;
+ BOOL found;
+ endp_addr = psetup->wIndex;
+ if( ( endp_addr & 0xf ) == 0 )
+ {
+ unlock_dev( pdev, FALSE );
+ status = STATUS_INVALID_PARAMETER;
+ goto LBL_OUT;
+ }
+
+ // search for the endp by the endp addr in the wIndex
+ found = FALSE;
+ for( if_idx = 0; if_idx < pdev->usb_config->if_count; if_idx++ )
+ {
+ for( endp_idx =0; endp_idx < pdev->usb_config->interf[ if_idx ].endp_count; endp_idx++ )
+ {
+ pendp = &pdev->usb_config->interf[ if_idx ].endp[ endp_idx ];
+ if( pendp->pusb_endp_desc->bEndpointAddress == endp_addr )
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ if( found == TRUE )
+ break;
+ }
+ if( found )
+ endp_handle = usb_make_handle( pdev->dev_id, if_idx, endp_idx );
+ else
+ {
+ unlock_dev( pdev, FALSE );
+ status = STATUS_INVALID_PARAMETER;
+ goto LBL_OUT;
+ }
+ unlock_dev( pdev, FALSE );
+ status = usb_reset_pipe_ex(
+ dev_mgr,
+ endp_handle,
+ usb_reset_pipe_from_dispatch_completion,
+ purb );
+
+ goto LBL_OUT;
+ }
+ }
+
+ unlock_dev( pdev, FALSE );
+ status = hcd->hcd_submit_urb( hcd, pdev, purb->pendp, purb );
+
+ LBL_OUT:
+ usb_unlock_dev( pdev );
+ return status;
+}
+
+
+void
+usb_config_dev_completion(
+PURB purb,
+PVOID context
+)
+{
+ PURB puser_urb;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ ULONG config_val;
+ NTSTATUS status;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ {
+ return;
+ }
+ pdev = purb->pdev;
+ pendp = purb->pendp;
+
+ if( pdev == NULL )
+ return;
+
+ if( purb->reference != 0 )
+ puser_urb = ( PURB ) purb->reference;
+ else
+ puser_urb = NULL;
+
+ lock_dev( pdev, TRUE );
+
+ if( puser_urb )
+ puser_urb->status = purb->status;
+
+ if( purb->status != STATUS_SUCCESS )
+ {
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ goto LBL_OUT;
+ }
+
+ if( dev_state( pdev ) == USB_DEV_STATE_FIRST_CONFIG )
+ {
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_ADDRESSED;
+ }
+ else if( dev_state( pdev ) == USB_DEV_STATE_RECONFIG )
+ {
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+
+ }
+ goto LBL_OUT;
+ }
+ // now let's construct usb_config
+ if( !pdev->usb_config )
+ {
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ config_val = psetup->wValue;
+ status = dev_mgr_build_usb_config( pdev,
+ &pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ],
+ config_val,
+ pdev->pusb_dev_desc->bNumConfigurations );
+ if( status != STATUS_SUCCESS )
+ {
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_ADDRESSED;
+ goto LBL_OUT;
+ }
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+ //this usb dev represents physical dev
+ if( pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_HUB
+ && pdev->pusb_dev_desc->bDeviceSubClass == 0 )
+ {
+ pdev->flags &= ~USB_DEV_CLASS_MASK;
+ pdev->flags |= USB_DEV_CLASS_HUB;
+ }
+ else if( pdev->pusb_dev_desc->bDeviceClass == USB_CLASS_MASS_STORAGE
+ && pdev->pusb_dev_desc->bDeviceSubClass == 0 )
+ {
+ pdev->flags &= ~USB_DEV_CLASS_MASK;
+ pdev->flags |= USB_DEV_CLASS_MASSSTOR;
+ }
+ else
+ {
+ pdev->flags &= ~USB_DEV_CLASS_MASK;
+ pdev->flags |= USB_DEV_CLASS_SCANNER;
+ }
+ }
+ else
+ {
+ //not supported
+ puser_urb->status = STATUS_NOT_SUPPORTED;
+ pdev->flags &= ~USB_DEV_STATE_MASK;
+ pdev->flags |= USB_DEV_STATE_CONFIGURED;
+ }
+ LBL_OUT:
+ unlock_dev( pdev, TRUE );
+ usb_free_mem( purb );
+ if( puser_urb && puser_urb->completion )
+ puser_urb->completion( puser_urb, puser_urb->context );
+
+ return;
+}
+
+NTSTATUS
+usb_get_descriptor(
+PUSB_DEV pdev,
+PURB purb
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ LONG idx, size, count, i;
+ PBYTE buf;
+ PUSB_CONFIGURATION_DESC pcfg_desc1;
+
+ USE_IRQL;
+
+ if( pdev == NULL || purb == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if( purb->data_buffer == NULL || purb->data_length == 0 )
+ {
+ return purb->status = STATUS_INVALID_PARAMETER;
+ }
+
+ lock_dev( pdev, FALSE );
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ if( pdev->desc_buf == NULL )
+ {
+ purb->status = STATUS_DEVICE_NOT_READY;
+ goto LBL_OUT;
+ }
+
+ if( ( psetup->wValue >> 8 ) == USB_DT_CONFIG )
+ {
+ idx = ( psetup->wValue & 0xff );
+
+ count = pdev->pusb_dev_desc->bNumConfigurations;
+
+ if( idx >= count )
+ {
+ purb->status = STATUS_INVALID_PARAMETER;
+ goto LBL_OUT;
+ }
+ buf = &pdev->desc_buf[ sizeof( USB_DEVICE_DESC ) ];
+ pcfg_desc1 = usb_find_config_desc_by_idx( buf, idx, count );
+ if( pcfg_desc1 == NULL )
+ {
+ purb->status = STATUS_UNSUCCESSFUL;
+ goto LBL_OUT;
+ }
+
+ size = pcfg_desc1->wTotalLength;
+ size = size > purb->data_length ? purb->data_length : size;
+ for( i = 0; i < size; i++ )
+ {
+ purb->data_buffer[ i ] = ( ( PBYTE )pcfg_desc1 )[ i ];
+ }
+ purb->status = STATUS_SUCCESS;
+ goto LBL_OUT;
+
+ }
+ else if(( psetup->wValue >> 8 ) == USB_DT_DEVICE )
+ {
+ size = purb->data_length > sizeof( USB_DEVICE_DESC )
+ ? sizeof( USB_DEVICE_DESC )
+ : purb->data_length;
+
+ for( i = 0; i < size; i ++ )
+ {
+ purb->data_buffer[ i ] = ( ( PBYTE )pdev->pusb_dev_desc )[ i ];
+ }
+ purb->status = STATUS_SUCCESS;
+ }
+LBL_OUT:
+ unlock_dev( pdev, FALSE );
+ return purb->status;
+}
+
+LONG usb_count_list( PLIST_HEAD list_head )
+{
+ LONG count;
+ PLIST_ENTRY pthis, pnext;
+
+ if( list_head == NULL )
+ return 0;
+
+ count = 0;
+ ListFirst( list_head, pthis );
+
+ while( pthis )
+ {
+ ListNext( list_head, pthis, pnext );
+ pthis = pnext;
+ count ++;
+ }
+ return count;
+}
+
+__inline BOOL
+usb_query_clicks(
+PLARGE_INTEGER clicks
+)
+{
+ BOOL ret_val;
+ //so we have to use intel's cpu???
+ ret_val = FALSE;
+ __asm
+ {
+ push ebx;
+ push eax;
+ mov eax, 1; //read version
+ cpuid;
+ test edx, 0x10; //timer stamp
+ jz LBL_OUT;
+ // cpuid //serialization
+ rdtsc;
+ mov ebx, dword ptr [ clicks ];
+ mov dword ptr [ ebx ], eax;
+ mov dword ptr [ ebx + 4 ], edx;
+ mov dword ptr [ ret_val ], TRUE;
+LBL_OUT:
+ pop eax;
+ pop ebx;
+ }
+ return ret_val;
+}
+
+VOID
+usb_wait_ms_dpc(
+ULONG ms
+)
+{
+ LARGE_INTEGER start;
+ LARGE_INTEGER ticker;
+ LARGE_INTEGER freq;
+ ULONG interval;
+ ULONG expire_count;
+
+ KeQueryPerformanceCounter( &freq );
+
+ expire_count = 2000000;
+ if( cpu_clock_freq )
+ expire_count = ( cpu_clock_freq / 1000 ) * ms;
+
+ if( usb_query_clicks( &start ) == FALSE )
+ {
+ ticker.QuadPart = 0;
+ while( TRUE )
+ {
+ KeQuerySystemTime( &ticker );
+ interval = ticker.LowPart - start.LowPart;
+ if( interval >= ms * 10000 )
+ break;
+ }
+ }
+ else
+ {
+ ticker.QuadPart = 0;
+ while( TRUE )
+ {
+ usb_query_clicks( &ticker );
+ interval = ticker.LowPart - start.LowPart;
+ if( interval >= expire_count )
+ break;
+ }
+ }
+}
+
+
+VOID
+usb_wait_us_dpc(
+ULONG us
+)
+{
+ LARGE_INTEGER start;
+ LARGE_INTEGER ticker;
+ LARGE_INTEGER freq;
+ ULONG interval;
+ ULONG expire_count;
+
+ KeQueryPerformanceCounter( &freq );
+
+ expire_count = 2000000;
+ if( cpu_clock_freq )
+ expire_count = ( cpu_clock_freq / 1000000 ) * us;
+
+ if( usb_query_clicks( &start ) == FALSE )
+ {
+ ticker.QuadPart = 0;
+ while( TRUE )
+ {
+ KeQuerySystemTime( &ticker );
+ interval = ticker.LowPart - start.LowPart;
+ if( interval >= us * 10 )
+ break;
+ }
+ }
+ else
+ {
+ ticker.QuadPart = 0;
+ while( TRUE )
+ {
+ usb_query_clicks( &ticker );
+ interval = ticker.LowPart - start.LowPart;
+ if( interval >= expire_count )
+ break;
+ }
+ }
+}
+
+VOID
+usb_cal_cpu_freq()
+{
+ LARGE_INTEGER tick1, tick2;
+ LARGE_INTEGER interval;
+ LONG i;
+ // interval.QuadPart = -40 * 1000 * 1000;
+
+ if( cpu_clock_freq >= 100 * 1000 * 1000 ) // assume it is valid
+ return;
+
+ if( usb_query_clicks( &tick1 ) )
+ {
+ for( i = 0; i < 25; i++ )
+ {
+ usb_query_clicks( &tick1 );
+ KeStallExecutionProcessor( 40 * 1000 );
+ usb_query_clicks( &tick2 );
+ cpu_clock_freq += ( ULONG )( tick2.QuadPart - tick1.QuadPart );
+ }
+ // cpu_clock_freq *= 1000;
+ usb_dbg_print( DBGLVL_MAXIMUM, ( "usb_cal_cpu_freq(): cpu frequency = %d Hz\n", cpu_clock_freq ) );
+ }
+}
+
+NTSTATUS
+usb_set_interface(
+PURB purb
+)
+{
+ ULONG u;
+ PURB purb1;
+ PCTRL_REQ_STACK pstack;
+ PUSB_DEV pdev;
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_ENDPOINT pendp;
+ NTSTATUS status;
+
+ PHCD hcd;
+ USE_IRQL;
+
+ purb1 = purb;
+ pdev = purb->pdev;
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+
+ lock_dev( pdev, FALSE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_NOT_CONNECTED;
+ }
+ if( dev_state( pdev ) < USB_DEV_STATE_CONFIGURED )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_NOT_READY;
+ }
+
+ hcd = pdev->hcd;
+
+ if( psetup->wIndex >= pdev->usb_config->if_count )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_INVALID_PARAMETER;
+ }
+ if( psetup->wValue >= pdev->usb_config->interf[ psetup->wIndex ].altif_count + 1 )
+ {
+ unlock_dev( pdev, FALSE );
+ return STATUS_INVALID_PARAMETER;
+ }
+ if( pdev->usb_config->interf[ psetup->wIndex ].pusb_if_desc->bAlternateSetting == psetup->wValue )
+ {
+ // already the current interface
+ unlock_dev( pdev, FALSE );
+ return STATUS_SUCCESS;
+ }
+ // check to see if the endp is busy
+ for( u = 0; u < pdev->usb_config->interf[ psetup->wIndex ].endp_count; u++ )
+ {
+ // This check is not adquate. Since we do not have mechanism to block the new coming
+ // request during this request. the caller must guarantee no active or pending
+ // usb request on these endpoint.
+ pendp = &pdev->usb_config->interf[ psetup->wIndex ].endp[ u ];
+ if( usb_endp_busy_count( pendp ) )
+ {
+ // active urb on that endp
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_NOT_READY;
+ }
+ if( IsListEmpty( &pendp->urb_list ) )
+ {
+ // pending urb on that endp
+ unlock_dev( pdev, FALSE );
+ return STATUS_DEVICE_NOT_READY;
+ }
+ }
+ unlock_dev( pdev, FALSE );
+
+ if( purb1->ctrl_req_context.ctrl_stack_count == 0 )
+ {
+ // ok, we have one stack cell for our use
+ if( purb1->completion != NULL )
+ {
+ purb1->ctrl_req_context.ctrl_stack_count = 1;
+ purb1->ctrl_req_context.ctrl_cur_stack = 0;
+ }
+ else
+ {
+ // use urb's completion and context
+ purb1->completion = usb_set_interface_completion;
+ purb1->context = pdev;
+ }
+ }
+ else
+ {
+ if( purb->ctrl_req_context.ctrl_cur_stack + 1 >= purb->ctrl_req_context.ctrl_stack_count )
+ {
+ // stack full, let's allocate one new urb, we need stack size one
+ purb1 = usb_alloc_mem( NonPagedPool, sizeof( URB ) );
+ if( purb1 == NULL )
+ return STATUS_NO_MEMORY;
+
+ RtlCopyMemory( purb1, purb, sizeof( URB ) );
+
+ // we do not use stack
+ RtlZeroMemory( purb1->ctrl_req_stack, sizeof( CTRL_REQ_STACK ) );
+ purb1->context = pdev;
+ purb1->completion = usb_set_interface_completion;
+ purb1->ctrl_parent_urb = purb;
+ purb1->ctrl_req_context.ctrl_req_flags = CTRL_PARENT_URB_VALID;
+
+ goto LBL_SEND_URB;
+ }
+ else
+ purb->ctrl_req_context.ctrl_cur_stack++;
+ }
+
+ u = purb1->ctrl_req_context.ctrl_cur_stack;
+ RtlZeroMemory( &purb1->ctrl_req_stack[ u ], sizeof( CTRL_REQ_STACK ) );
+ pstack = &purb1->ctrl_req_stack[ u ];
+ pstack->context = pdev;
+ pstack->urb_completion = usb_set_interface_completion;
+
+LBL_SEND_URB:
+ if( hcd == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ status = hcd->hcd_submit_urb( hcd, purb->pdev, purb->pendp, purb );
+ return status;
+}
+
+#define usb_complete_and_free_ctrl_urb( pURB ) \
+{\
+ UCHAR i, j;\
+ i = pURB->ctrl_req_context.ctrl_cur_stack;\
+ j = pURB->ctrl_req_context.ctrl_stack_count;\
+ usb_call_ctrl_completion( pURB );\
+ if( i == 0xff || j == 0 )\
+ usb_free_mem( pURB );\
+}
+
+VOID
+usb_set_interface_completion(
+PURB purb,
+PVOID context
+)
+{
+ PUSB_CTRL_SETUP_PACKET psetup;
+ PUSB_INTERFACE pif, palt_if;
+ USB_INTERFACE temp_if;
+ UCHAR if_idx, if_alt_idx;
+ PUSB_DEV pdev;
+ PUSB_ENDPOINT pendp;
+ ULONG i;
+ PLIST_ENTRY pthis, pnext;
+
+ USE_IRQL;
+
+ if( purb == NULL )
+ return;
+
+ if( purb->status == STATUS_SUCCESS )
+ {
+ psetup = ( PUSB_CTRL_SETUP_PACKET )purb->setup_packet;
+ if_idx = ( UCHAR )psetup->wIndex;
+ if_alt_idx = ( UCHAR )psetup->wValue;
+ pdev = purb->pdev;
+ RtlZeroMemory( &temp_if, sizeof( USB_INTERFACE ) );
+
+ lock_dev( pdev, TRUE );
+ if( dev_state( pdev ) == USB_DEV_STATE_ZOMB )
+ {
+ unlock_dev( pdev, TRUE );
+ purb->status = STATUS_DEVICE_NOT_CONNECTED;
+ purb->data_length = 0;
+ }
+ else
+ {
+ // let's swap the interface
+ pif = &pdev->usb_config->interf[ if_idx ];
+ ListFirst( &pif->altif_list, pthis );
+ pnext = pthis;
+ do
+ {
+ palt_if = struct_ptr( pthis, USB_INTERFACE, altif_list );
+ if( palt_if->pusb_if_desc->bAlternateSetting == if_alt_idx )
+ {
+ break;
+ }
+ palt_if = NULL;
+ ListNext( &pif->altif_list, pthis, pnext );
+ pthis = pnext;
+
+ }while( pthis );
+
+ if( palt_if != NULL )
+ {
+ RtlCopyMemory( &temp_if, palt_if, sizeof( USB_INTERFACE ) );
+
+ palt_if->endp_count = pif->endp_count;
+ RtlCopyMemory( palt_if->endp, pif->endp, sizeof( pif->endp ) );
+ palt_if->pif_drv = pif->pif_drv;
+ palt_if->pusb_if_desc = pif->pusb_if_desc;
+ for( i = 0; i < palt_if->endp_count; i++ )
+ {
+ pendp = &palt_if->endp[ i ];
+ InitializeListHead( &pendp->urb_list );
+ pendp->flags = 0;
+ }
+
+ RtlCopyMemory( pif->endp, temp_if.endp, sizeof( temp_if.endp ) );
+ pif->endp_count = temp_if.endp_count;
+ pif->pusb_if_desc = temp_if.pusb_if_desc;
+ for( i = 0; i < pif->endp_count; i++ )
+ {
+ pendp = &pif->endp[ i ];
+ InitializeListHead( &pendp->urb_list );
+ pendp->flags = 0;
+ }
+ }
+ else
+ {
+ TRAP();
+ purb->status = STATUS_UNSUCCESSFUL;
+ }
+ }
+ unlock_dev( pdev, TRUE );
+ }
+
+ // for recursive reason, we have to store the parameter ahead
+ usb_complete_and_free_ctrl_urb( purb );
+}
+
+VOID
+usb_call_ctrl_completion(
+PURB purb
+)
+// can only be called when current completion finished and called only in
+// urb completion. And this func may be called recursively, if this routine
+// is called, the urb must be treated as released.
+{
+ PURB parent_urb;
+ PCTRL_REQ_STACK pstack;
+ ULONG i;
+
+
+ if( purb == NULL )
+ return;
+
+ if( purb->ctrl_req_context.ctrl_stack_count != 0 )
+ {
+ i = purb->ctrl_req_context.ctrl_cur_stack;
+ if( i > 0 && i < 0x10 )
+ {
+ i --;
+ purb->ctrl_req_context.ctrl_cur_stack = ( UCHAR )i;
+ pstack = &purb->ctrl_req_stack[ i ];
+ if( pstack->urb_completion )
+ {
+ pstack->urb_completion( purb, pstack->context );
+ }
+ else
+ TRAP();
+ }
+ else if( i == 0 )
+ {
+ i = purb->ctrl_req_context.ctrl_cur_stack = 0xff;
+ if( purb->completion )
+ {
+ purb->completion( purb, purb->context );
+ }
+ else
+ TRAP();
+ }
+ else if( i == 0xff )
+ {
+ // only parent urb's completion, if parent urb exists, can be called
+ if( purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID )
+ {
+ parent_urb = purb->ctrl_parent_urb;
+ if( parent_urb )
+ {
+ pstack = &parent_urb->ctrl_req_stack[ parent_urb->ctrl_req_context.ctrl_cur_stack ];
+ pstack->urb_completion( parent_urb, pstack->context );
+ }
+ else
+ TRAP();
+ }
+ }
+ else
+ TRAP();
+ }
+ else if( purb->ctrl_req_context.ctrl_req_flags & CTRL_PARENT_URB_VALID )
+ {
+ // this is the case when the child urb won't use the stack
+ parent_urb = purb->ctrl_parent_urb;
+ if( parent_urb )
+ {
+ // pstack = &parent_urb->ctrl_req_stack[ parent_urb->ctrl_req_context.ctrl_cur_stack ];
+ // pstack->urb_completion( parent_urb, pstack->context );
+ usb_call_ctrl_completion( parent_urb );
+ }
+ else
+ TRAP();
+ }
+ else
+ return;
+}
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usb.h b/reactos/drivers/usb/nt4compat/usbdriver/usb.h
new file mode 100644
index 00000000000..5fd7c55d0ba
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/usb.h
@@ -0,0 +1,1062 @@
+#ifndef __USBD_H__
+#define __USBD_H__
+#include "ntddk.h"
+/*
+ * Some USB bandwidth allocation constants.
+ */
+
+#ifndef BOOL
+#define BOOL ULONG
+#endif
+
+#define USB2_HOST_DELAY 5 /* nsec, guess */
+#define BW_HOST_DELAY 1000L /* nanoseconds */
+#define BW_HUB_LS_SETUP 333L /* nanoseconds */
+ /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_USECS 1000L
+#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
+
+#define bit_time(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
+ /* Trying not to use worst-case bit-stuffing
+ of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+ /* bytecount = data payload byte count */
+
+#define ns_to_us(ns) ((ns + 500L) / 1000L)
+ /* convert & round nanoseconds to microseconds */
+
+#define usb_make_handle( dev_Id, if_iDx, endp_iDx) \
+( ( DEV_HANDLE )( ( ( ( ( ULONG )dev_Id ) << 16 ) | ( ( ( ULONG )if_iDx ) << 8 ) ) | ( ( ULONG ) endp_iDx ) ) )
+
+#define usb_make_ref( poinTER ) \
+( poinTER ^ 0xffffffff )
+
+#define ptr_from_ref uhci_make_ref
+
+#define dev_id_from_handle( hanDLE ) ( ( ( ULONG ) ( hanDLE ) ) >> 16 )
+#define if_idx_from_handle( hanDLE ) ( ( ( ( ULONG ) ( hanDLE ) ) << 16 ) >> 24 )
+#define endp_idx_from_handle( hanDLE ) ( ( ( ULONG ) ( hanDLE ) ) & 0xff )
+
+#define endp_from_handle( pDEV, hanDLE, peNDP ) \
+{\
+ LONG if_idx, endp_idx;\
+ BOOL def_endp; \
+ endp_idx = endp_idx_from_handle( hanDLE );\
+ if_idx = if_idx_from_handle( hanDLE );\
+ def_endp = ( ( hanDLE & 0xffff ) == 0xffff ); \
+ if( def_endp ) \
+ peNDP = &pdev->default_endp; \
+ else \
+ { \
+ if( if_idx >= pdev->usb_config->if_count ) \
+ peNDP = NULL; \
+ else if( endp_idx >= pdev->usb_config->interf[ if_idx ].endp_count ) \
+ peNDP = NULL; \
+ else \
+ peNDP = &( pDEV )->usb_config->interf[ if_idx ].endp[ endp_idx ]; \
+ } \
+}
+
+#define endp_type( enDP ) \
+( ( enDP->flags & USB_ENDP_FLAG_DEFAULT_ENDP ) \
+ ? USB_ENDPOINT_XFER_CONTROL\
+ : ( ( enDP )->pusb_endp_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) )
+
+
+//init work data for urb
+#define urb_init( uRb ) \
+{\
+ RtlZeroMemory( ( uRb ), sizeof( URB ) ); \
+ InitializeListHead( &( uRb )->trasac_list ); \
+}
+
+#define UsbBuildInterruptOrBulkTransferRequest(uRb, \
+ endp_hanDle, \
+ data_Buf, \
+ data_sIze, \
+ completIon, \
+ contExt, \
+ refereNce ) \
+{ \
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = endp_hanDle;\
+ ( uRb )->data_buffer = data_Buf;\
+ ( uRb )->data_length = data_sIze;\
+ ( uRb )->completion = completIon;\
+ ( uRb )->context = contExt; \
+ ( uRb )->reference = refereNce; \
+}
+
+
+
+#define UsbBuildGetDescriptorRequest(uRb, \
+ endp_hAndle, \
+ descriPtorType, \
+ descriPtorIndex, \
+ languaGeId, \
+ data_bUffer, \
+ data_sIze, \
+ compleTion, \
+ contexT, \
+ refereNce ) \
+{ \
+ PUSB_CTRL_SETUP_PACKET pseTup;\
+ pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hAndle );\
+ ( uRb )->data_length = ( data_sIze ); \
+ ( uRb )->data_buffer = ( ( PUCHAR )data_bUffer ); \
+ ( uRb )->completion = ( compleTion );\
+ ( uRb )->context = ( ( PVOID )contexT ); \
+ ( uRb )->reference = ( ULONG )refereNce; \
+ pseTup->wValue = ( ( descriPtorType ) << 8 )| ( descriPtorIndex ); \
+ pseTup->wLength = ( data_sIze ); \
+ pseTup->wIndex = ( languaGeId );\
+ pseTup->bRequest = USB_REQ_GET_DESCRIPTOR;\
+ pseTup->bmRequestType = 0x80;\
+}
+
+
+
+#define UsbBuildGetStatusRequest(uRb, \
+ endp_hanDle, \
+ recipiEnt, \
+ inDex, \
+ transferBufFer, \
+ completIon, \
+ contExt, \
+ refereNce ) \
+{ \
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hanDle ); \
+ ( uRb )->data_buffer = ( transferBufFer ); \
+ ( uRb )->data_length = sizeof(USHORT); \
+ ( uRb )->compleiton = ( completIon );\
+ ( uRb )->context = ( contExt );\
+ ( uRb )->reference = ( refereNce );\
+ pseTup->bmRequestType = ( 0x80 | recipiEnt );\
+ pseTup->bRequest = USB_REQ_GET_STATUS;\
+ pseTup->wIndex = ( index ); \
+ pseTup->wValue = 0;\
+ pseTup->wLength = sizeof( USHORT );\
+}
+
+
+#define UsbBuildFeatureRequest(uRb, \
+ endp_hanDle,\
+ recipiEnt, \
+ featureSelecTor, \
+ inDex, \
+ completIon, \
+ contExt, \
+ refereNce ) \
+ { \
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hanDle ); \
+ ( uRb )->data_buffer = NULL;\
+ ( uRb )->data_length = ( 0 );\
+ ( uRb )->completion = ( completIon );\
+ ( uRb )->context = ( contExt ); \
+ ( uRb )->reference = ( refereNce ); \
+ pseTup->bmRequestType = recipiEnt; \
+ pseTup->bRequest = USB_REQ_SET_FEATURE;\
+ pseTup->wValue = ( featureSelecTor );\
+ pseTup->wIndex = ( inDex );\
+ pseTup->wLength = 0;\
+}
+
+#define UsbBuildSelectConfigurationRequest(uRb, \
+ endp_hanDle,\
+ config_Val,\
+ completIon, \
+ contExt, \
+ refereNce ) \
+ { \
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hanDle ); \
+ ( uRb )->data_buffer = NULL;\
+ ( uRb )->data_length = 0;\
+ ( uRb )->completion = ( completIon );\
+ ( uRb )->context = ( contExt ); \
+ ( uRb )->reference = ( refereNce ); \
+ pseTup->bmRequestType = 0;\
+ pseTup->bRequest = USB_REQ_SET_CONFIGURATION;\
+ pseTup->wValue = ( config_Val );\
+ pseTup->wIndex = 0;\
+ pseTup->wLength = 0;\
+}
+
+#define UsbBuildSelectInterfaceRequest(uRb, \
+ endp_hanDle,\
+ if_Num, \
+ alt_Num,\
+ completIon, \
+ contExt, \
+ refereNce ) \
+ { \
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hanDle ); \
+ ( uRb )->data_buffer = NULL;\
+ ( uRb )->data_length = 0;\
+ ( uRb )->completion = ( completIon );\
+ ( uRb )->context = ( contExt ); \
+ ( uRb )->reference = ( refereNce ); \
+ pseTup->bmRequestType = 1;\
+ pseTup->bRequest = USB_REQ_SET_INERFACE;\
+ pseTup->wValue = ( alt_Num );\
+ pseTup->wIndex = ( if_Num );\
+ pseTup->wLength = 0;\
+}
+
+
+#define UsbBuildVendorRequest(uRb, \
+ endp_hanDle,\
+ data_bufFer, \
+ data_sIze, \
+ request_tYpe, \
+ requEst, \
+ vaLue, \
+ inDex, \
+ completIon, \
+ contExt, \
+ refereNce ) \
+ { \
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( endp_hanDle ); \
+ ( uRb )->data_buffer = data_bufFer;\
+ ( uRb )->data_length = data_sIze;\
+ ( uRb )->completion = ( completIon );\
+ ( uRb )->context = ( contExt ); \
+ ( uRb )->reference = ( refereNce ); \
+ pseTup->bmRequestType = request_tYpe;\
+ pseTup->bRequest = requEst;\
+ pseTup->wValue = vaLue;\
+ pseTup->wIndex = inDex;\
+ pseTup->wLength = ( USHORT )data_sIze;\
+}
+
+#define UsbBuildResetPipeRequest(uRb, \
+ dev_hanDle, \
+ endp_aDdr, \
+ completIon, \
+ contExt, \
+ refereNce ) \
+{\
+ PUSB_CTRL_SETUP_PACKET pseTup = ( PUSB_CTRL_SETUP_PACKET )( uRb )->setup_packet;\
+ urb_init( ( uRb ) );\
+ ( uRb )->endp_handle = ( dev_hanDle | 0xffff ); \
+ ( uRb )->completion = ( completIon );\
+ ( uRb )->context = ( contExt ); \
+ ( uRb )->reference = ( refereNce ); \
+ pseTup->bmRequestType = 0x02;\
+ pseTup->bRequest = USB_REQ_CLEAR_FEATURE;\
+ pseTup->wIndex = endp_aDdr;\
+}
+
+
+
+/* USB constants */
+
+#define USB_SPEED_FULL 0x00
+#define USB_SPEED_LOW 0x01
+#define USB_SPEED_HIGH 0x02
+
+/*
+ * Device and/or Interface Class codes
+ */
+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_DATA 10
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+/*
+ * USB types
+ */
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+/*
+ * USB recipients
+ */
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/*
+ * USB directions
+ */
+#define USB_DIR_OUT 0
+#define USB_DIR_IN 0x80
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+
+#define USB_DT_HID (USB_TYPE_CLASS | 0x01)
+#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02)
+#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
+#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
+
+/*
+ * Descriptor sizes per descriptor type
+ */
+#define USB_DT_DEVICE_SIZE 18
+#define USB_DT_CONFIG_SIZE 9
+#define USB_DT_INTERFACE_SIZE 9
+#define USB_DT_ENDPOINT_SIZE 7
+#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE 7
+#define USB_DT_HID_SIZE 9
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK 0x80
+
+#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
+#define USB_ENDPOINT_XFER_CONTROL 0
+#define USB_ENDPOINT_XFER_ISOC 1
+#define USB_ENDPOINT_XFER_BULK 2
+#define USB_ENDPOINT_XFER_INT 3
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0 0xf0
+#define USB_PID_OUT 0xe1
+#define USB_PID_ACK 0xd2
+#define USB_PID_DATA0 0xc3
+#define USB_PID_PING 0xb4 /* USB 2.0 */
+#define USB_PID_SOF 0xa5
+#define USB_PID_NYET 0x96 /* USB 2.0 */
+#define USB_PID_DATA2 0x87 /* USB 2.0 */
+#define USB_PID_SPLIT 0x78 /* USB 2.0 */
+#define USB_PID_IN 0x69
+#define USB_PID_NAK 0x5a
+#define USB_PID_DATA1 0x4b
+#define USB_PID_PREAMBLE 0x3c /* Token mode */
+#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
+#define USB_PID_SETUP 0x2d
+#define USB_PID_STALL 0x1e
+#define USB_PID_MDATA 0x0f /* USB 2.0 */
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+/*
+ * HID requests
+ */
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_GET_IDLE 0x02
+#define USB_REQ_GET_PROTOCOL 0x03
+#define USB_REQ_SET_REPORT 0x09
+#define USB_REQ_SET_IDLE 0x0A
+#define USB_REQ_SET_PROTOCOL 0x0B
+
+// HUB request
+#define HUB_REQ_GET_STATE 0x02
+
+// usb2.0 hub
+#define HUB_REQ_CLEAR_TT_BUFFER 0x08
+
+
+typedef LONG USBD_STATUS;
+
+//
+// USBD status codes
+//
+// Status values are 32 bit values layed out as follows:
+//
+// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+// +---+---------------------------+-------------------------------+
+// | S | Status Code |
+// +---+---------------------------+-------------------------------+
+//
+// where
+//
+// S - is the state code
+//
+// 00 - completed with success
+// 01 - request is pending
+// 10 - completed with error, endpoint not stalled
+// 11 - completed with error, endpoint stalled
+//
+//
+// Code - is the status code
+//
+
+//
+// Generic test for success on any status value (non-negative numbers
+// indicate success).
+//
+
+#define usb_success(Status) ((USBD_STATUS)(Status) >= 0)
+
+//
+// Generic test for pending status value.
+//
+
+#define usb_pending(Status) ((ULONG)(Status) >> 30 == 1)
+
+//
+// Generic test for error on any status value.
+//
+
+#define usb_error(Status) ((USBD_STATUS)(Status) < 0)
+
+//
+// Generic test for stall on any status value.
+//
+
+#define usb_halted(Status) ((ULONG)(Status) >> 30 == 3)
+
+//
+// Macro to check the status code only
+//
+
+#define usb_status(Status) ((ULONG)(Status) & 0x0FFFFFFFL)
+
+
+#define USB_STATUS_SUCCESS ((USBD_STATUS)0x00000000L)
+#define USB_STATUS_PENDING ((USBD_STATUS)0x40000000L)
+#define USB_STATUS_HALTED ((USBD_STATUS)0xC0000000L)
+#define USB_STATUS_ERROR ((USBD_STATUS)0x80000000L)
+
+//
+// HC status codes
+// Note: these status codes have both the error and the stall bit set.
+//
+#define USB_STATUS_CRC ((USBD_STATUS)0xC0000401L)
+#define USB_STATUS_BTSTUFF ((USBD_STATUS)0xC0000402L)
+#define USB_STATUS_DATA_TOGGLE_MISMATCH ((USBD_STATUS)0xC0000403L)
+#define USB_STATUS_STALL_PID ((USBD_STATUS)0xC0000404L)
+#define USB_STATUS_DEV_NOT_RESPONDING ((USBD_STATUS)0xC0000405L)
+#define USB_STATUS_PID_CHECK_FAILURE ((USBD_STATUS)0xC0000406L)
+#define USB_STATUS_UNEXPECTED_PID ((USBD_STATUS)0xC0000407L)
+#define USB_STATUS_DATA_OVERRUN ((USBD_STATUS)0xC0000408L)
+#define USB_STATUS_DATA_UNDERRUN ((USBD_STATUS)0xC0000409L)
+#define USB_STATUS_RESERVED1 ((USBD_STATUS)0xC000040AL)
+#define USB_STATUS_RESERVED2 ((USBD_STATUS)0xC000040BL)
+#define USB_STATUS_BUFFER_OVERRUN ((USBD_STATUS)0xC000040CL)
+#define USB_STATUS_BUFFER_UNDERRUN ((USBD_STATUS)0xC000040DL)
+#define USB_STATUS_NOT_ACCESSED ((USBD_STATUS)0xC000040FL)
+#define USB_STATUS_FIFO ((USBD_STATUS)0xC0000410L)
+#define USB_STATUS_BABBLE_DETECTED ((USBD_STATUS)0xC0000408L)
+
+//
+// returned by HCD if a transfer is submitted to an endpoint that is
+// stalled
+//
+#define USB_STATUS_ENDPOINT_HALTED ((USBD_STATUS)0xC0000430L)
+
+//
+// Software status codes
+// Note: the following status codes have only the error bit set
+//
+#define USB_STATUS_NO_MEMORY ((USBD_STATUS)0x80000100L)
+#define USB_STATUS_INVALID_URB_FUNCTION ((USBD_STATUS)0x80000200L)
+#define USB_STATUS_INVALID_PARAMETER ((USBD_STATUS)0x80000300L)
+
+//
+// returned if client driver attempts to close an endpoint/interface
+// or configuration with outstanding transfers.
+//
+#define USB_STATUS_ERROR_BUSY ((USBD_STATUS)0x80000400L)
+//
+// returned by USBD if it cannot complete a URB request, typically this
+// will be returned in the URB status field when the Irp is completed
+// with a more specific NT error code in the irp.status field.
+//
+#define USB_STATUS_REQUEST_FAILED ((USBD_STATUS)0x80000500L)
+
+#define USB_STATUS_INVALID_PIPE_HANDLE ((USBD_STATUS)0x80000600L)
+
+// returned when there is not enough bandwidth avialable
+// to open a requested endpoint
+#define USB_STATUS_NO_BANDWIDTH ((USBD_STATUS)0x80000700L)
+//
+// generic HC error
+//
+#define USB_STATUS_INTERNAL_HC_ERROR ((USBD_STATUS)0x80000800L)
+//
+// returned when a short packet terminates the transfer
+// ie USBD_SHORT_TRANSFER_OK bit not set
+//
+#define USB_STATUS_ERROR_SHORT_TRANSFER ((USBD_STATUS)0x80000900L)
+//
+// returned if the requested start frame is not within
+// USBD_ISO_START_FRAME_RANGE of the current USB frame,
+// note that the stall bit is set
+//
+#define USB_STATUS_BAD_START_FRAME ((USBD_STATUS)0xC0000A00L)
+//
+// returned by HCD if all packets in an iso transfer complete with an error
+//
+#define USB_STATUS_ISOCH_REQUEST_FAILED ((USBD_STATUS)0xC0000B00L)
+//
+// returned by USBD if the frame length control for a given
+// HC is already taken by anothe driver
+//
+#define USB_STATUS_FRAME_CONTROL_OWNED ((USBD_STATUS)0xC0000C00L)
+//
+// returned by USBD if the caller does not own frame length control and
+// attempts to release or modify the HC frame length
+//
+#define USB_STATUS_FRAME_CONTROL_NOT_OWNED ((USBD_STATUS)0xC0000D00L)
+
+//
+// set when a transfers is completed due to an AbortPipe request from
+// the client driver
+//
+// Note: no error or stall bit is set for these status codes
+//
+#define USB_STATUS_CANCELED ((USBD_STATUS)0x00010000L)
+
+#define USB_STATUS_CANCELING ((USBD_STATUS)0x00020000L)
+
+// Device type -- in the "User Defined" range."
+#define FILE_HCD_DEV_TYPE 45000
+#define FILE_UHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 1 )
+#define FILE_OHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 2 )
+#define FILE_EHCI_DEV_TYPE ( FILE_HCD_DEV_TYPE + 3 )
+#define FILE_USB_DEV_TYPE ( FILE_HCD_DEV_TYPE + 8 )
+
+#define IOCTL_GET_DEV_COUNT CTL_CODE( FILE_HCD_DEV_TYPE, 4093, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer and input_buffer_length is zero, output_buffer is to receive a dword value of the
+//dev count, output_buffer_length must be no less than sizeof( unsigned long ).
+
+#define IOCTL_ENUM_DEVICES CTL_CODE( FILE_HCD_DEV_TYPE, 4094, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer is a dword value to indicate the count of elements in the array
+//input_buffer_length is sizeof( unsigned long ), output_buffer is to receive a
+//structure ENUM_DEV_ARRAY where dev_count is the elements hold in this array.
+
+#define IOCTL_GET_DEV_DESC CTL_CODE( FILE_HCD_DEV_TYPE, 4095, METHOD_BUFFERED, FILE_ANY_ACCESS )
+//input_buffer is a structure GET_DEV_DESC_REQ, and the input_buffer_length is
+//no less than sizeof( input_buffer ), output_buffer is a buffer to receive the
+//requested dev's desc, and output_buffer_length specifies the length of the
+//buffer
+
+#define IOCTL_SUBMIT_URB_RD CTL_CODE( FILE_HCD_DEV_TYPE, 4096, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
+#define IOCTL_SUBMIT_URB_WR CTL_CODE( FILE_HCD_DEV_TYPE, 4097, METHOD_OUT_DIRECT, FILE_ANY_ACCESS )
+// if the major_function is IRP_MJ_DEVICE_CONTROL
+// input_buffer is a URB, and input_buffer_length is equal to or greater than
+// sizeof( URB ); the output_buffer is a buffer to receive data from or send data
+// to device. only the following urb fields can be accessed, others must be zeroed.
+// DEV_HANDLE endp_handle;
+// UCHAR setup_packet[8]; // for control pipe
+// the choosing of IOCTL_SUBMIT_URB_RD or IOCTL_SUBMIT_URB_WR should be determined
+// by the current URB, for example, a request string from device will use XXX_RD,
+// and a write to the bulk endpoint will use XXX_WR
+// if the major_function is IRP_MJ_INTERNAL_DEVICE_CONTROL
+// input_buffer is a URB, and input_buffer_length is equal to or greater than
+// sizeof( URB );
+// only the following urb fields can be accessed, others must be zeroed.
+// DEV_HANDLE endp_handle;
+// UCHAR setup_packet[8]; // for control pipe, or zeroed
+// PUCHAR data_buffer; // buffer for READ/WRITE
+// ULONG data_length; // buffer size in bytes
+
+#define IOCTL_SUBMIT_URB_NOIO CTL_CODE( FILE_HCD_DEV_TYPE, 4098, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// input_buffer is a URB, and input_buffer_length is equal to or greater than
+// sizeof( URB ); the output_buffer is null and no output_buffer_length,
+// only the following fields in urb can be accessed, others must be zeroed.
+// DEV_HANDLE endp_handle;
+// UCHAR setup_packet[8]; //for control pipe
+// there is no difference between IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL
+#define IOCTL_GET_DEV_HANDLE CTL_CODE( FILE_HCD_DEV_TYPE, 4099, METHOD_BUFFERED, FILE_ANY_ACCESS )
+// input_buffer is null ,and input_buffer_length is zero.
+// output_buffer will hold the handle to this dev, output_buffer_length is 4
+// or bigger
+
+typedef ULONG DEV_HANDLE, ENDP_HANDLE, IF_HANDLE;
+
+struct URB;
+#pragma pack( push, usb_align, 1 )
+
+//structures for DeviceIoControl
+typedef struct _ENUM_DEV_ELEMENT
+{
+ DEV_HANDLE dev_handle;
+ USHORT product_id;
+ USHORT vendor_id;
+ UCHAR dev_addr;
+
+} ENUM_DEV_ELEMENT, *PENUM_DEV_ELEMENT;
+
+typedef struct _ENUM_DEV_ARRAY
+{
+ UCHAR dev_count;
+ ENUM_DEV_ELEMENT dev_arr[ 1 ];
+
+} ENUM_DEV_ARRAY, *PENUM_DEV_ARRAY;
+
+typedef struct _GET_DEV_DESC_REQ
+{
+ DEV_HANDLE dev_handle;
+ UCHAR desc_type;
+ UCHAR desc_idx;
+
+} GET_DEV_DESC_REQ, *PGET_DEV_DESC_REQ;
+
+//usb definitions
+typedef struct _USB_CTRL_SETUP_PACKET
+{
+ UCHAR bmRequestType;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+
+}USB_CTRL_SETUP_PACKET, *PUSB_CTRL_SETUP_PACKET;
+
+typedef struct _USB_STRING_DESCRIPTOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ USHORT wData[1];
+
+} USB_STRING_DESCRIPTOR, *PUSB_STRING_DESCRIPTOR;
+
+typedef struct _USB_DESC_HEADER
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+
+} USB_DESC_HEADER, *PUSB_DESC_HEADER;
+
+typedef struct _USB_ENDPOINT_DESC
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bEndpointAddress;
+ UCHAR bmAttributes;
+ USHORT wMaxPacketSize;
+ UCHAR bInterval;
+
+} USB_ENDPOINT_DESC, *PUSB_ENDPOINT_DESC;
+
+typedef struct _USB_INTERFACE_DESC
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bInterfaceNumber;
+ UCHAR bAlternateSetting;
+ UCHAR bNumEndpoints;
+ UCHAR bInterfaceClass;
+ UCHAR bInterfaceSubClass;
+ UCHAR bInterfaceProtocol;
+ UCHAR iInterface;
+
+} USB_INTERFACE_DESC, *PUSB_INTERFACE_DESC;
+
+typedef struct _USB_CONFIGURATION_DESC
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ USHORT wTotalLength;
+ UCHAR bNumInterfaces;
+ UCHAR bConfigurationValue;
+ UCHAR iConfiguration;
+ UCHAR bmAttributes;
+ UCHAR MaxPower;
+
+} USB_CONFIGURATION_DESC, *PUSB_CONFIGURATION_DESC;
+
+typedef struct _USB_DEVICE_DESC
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ USHORT bcdUSB;
+ UCHAR bDeviceClass;
+ UCHAR bDeviceSubClass;
+ UCHAR bDeviceProtocol;
+ UCHAR bMaxPacketSize0;
+ USHORT idVendor;
+ USHORT idProduct;
+ USHORT bcdDevice;
+ UCHAR iManufacturer;
+ UCHAR iProduct;
+ UCHAR iSerialNumber;
+ UCHAR bNumConfigurations;
+
+} USB_DEVICE_DESC, *PUSB_DEVICE_DESC;
+
+
+#define URB_FLAG_STATE_MASK 0x0f
+#define URB_FLAG_STATE_PENDING 0x00
+#define URB_FLAG_STATE_IN_PROCESS 0x01
+#define URB_FLAG_STATE_FINISHED 0x02
+
+// USB2.0 state
+#define URB_FLAG_STATE_DOORBELL 0x03 // for async request removal
+#define URB_FLAG_STATE_WAIT_FRAME 0x04 // for sync request removal( for cancel only )
+#define URB_FLAG_STATE_ERROR 0x05
+
+#define URB_FLAG_IN_SCHEDULE 0x10
+#define URB_FLAG_FORCE_CANCEL 0x20
+#define URB_FLAG_SHORT_PACKET 0x80000000
+
+typedef struct _SPLIT_ISO_BUS_TIME
+{
+ USHORT bus_time;
+ USHORT start_uframe;
+
+} SPLIT_ISO_BUS_TIME, *PSPLIT_ISO_BUS_TIME;
+
+typedef struct _ISO_PACKET_DESC
+{
+ LONG offset;
+ LONG length; // expected length
+ LONG actual_length;
+ LONG status;
+ union
+ {
+ LONG bus_time; //opaque for client request of split iso, the bus_time is the start_uframe for each transaction
+ SPLIT_ISO_BUS_TIME params;
+ };
+
+} ISO_PACKET_DESC, *PISO_PACKET_DESC;
+
+#define CTRL_PARENT_URB_VALID 1
+
+typedef void ( *PURBCOMPLETION )( struct _URB *purb, PVOID pcontext);
+
+typedef struct _CTRL_REQ_STACK
+{
+ PURBCOMPLETION urb_completion; // the last step of the urb_completion is to call the
+ // the prevoius stack's callback if has, and the last
+ // one is purb->completion
+ PVOID context;
+ ULONG params[ 3 ];
+
+} CTRL_REQ_STACK, *PCTRL_REQ_STACK;
+
+#pragma pack( pop, usb_align )
+
+#include "td.h"
+
+typedef struct _URB_HS_PIPE_CONTENT
+{
+ ULONG trans_type : 2; // bit 0-1
+ ULONG mult_count : 2; // bit 2-3, used in high speed int and iso requests
+ ULONG reserved : 1; // bit 1
+ ULONG speed_high : 1; // bit 5
+ ULONG speed_low : 1; // bit 6
+ ULONG trans_dir : 1; // bit 7
+ ULONG dev_addr : 7; // bit 8-14
+ ULONG endp_addr : 4; // bit 15-18
+ ULONG data_toggle : 1; // bit 19
+ ULONG max_packet_size : 4; // bit 20-23 log( max_packet_size )
+ ULONG interval : 4; // bit 24-27 the same definition in USB2.0, for high or full/low speed
+ ULONG start_uframe : 3; // bit 28-30
+ ULONG reserved1 : 1; //
+
+} URB_HS_PIPE_CONTENT, *PURB_HS_PIPE_CONTENT;
+
+typedef struct _URB_HS_CONTEXT_CONTENT
+{
+ ULONG hub_addr : 7; // high speed hub addr for split transfer
+ ULONG port_idx : 7;
+ ULONG reserved : 18;
+
+} URB_HS_CONTEXT_CONTENT, *PURB_HS_CONTEXT_CONTENT;
+
+typedef struct _URB
+{
+ LIST_ENTRY urb_link;
+ ULONG flags;
+ DEV_HANDLE endp_handle;
+ LONG status;
+ //record info for isr use, similar to td.status
+ //int pipe has different content in the 8 msb
+ //the eight bits contain interrupt interval.
+ //and max packet length is encoded in 3 bits from 23-21
+ //that means 2^(x) bytes in the packet.
+ ULONG pipe; // bit0-1: endp type, bit 6: ls or fs. bit 7: dir
+
+ union
+ {
+ UCHAR setup_packet[8]; // for control
+ LONG params[ 2 ]; // params[ 0 ] is used in iso transfer as max_packet_size
+ };
+
+ PUCHAR data_buffer; //user data
+ LONG data_length; //user data length
+
+ struct _USB_DEV *pdev;
+ struct _USB_ENDPOINT *pendp; //pipe for current transfer
+
+ PURBCOMPLETION completion;
+ PVOID context; //parameter of completion
+
+ PVOID urb_ext; //for high speed hcd use
+ ULONG hs_context; //for high speed hcd use
+
+ PIRP pirp; //irp from client driver
+ LONG reference; //for caller private use
+
+ LONG td_count; //for any kinds of transfer
+ LONG rest_bytes; //for split bulk transfer involving more than 1024 tds
+ LONG bytes_to_transfer;
+ LONG bytes_transfered; //( bulk transfer )accumulate one split-transfer by xfered bytes of the executed transactions
+ PLIST_ENTRY last_finished_td; //last inactive td useful for large amount transfer
+ LIST_ENTRY trasac_list; //list of tds or qhs
+
+ union
+ {
+ LONG iso_start_frame; // for high speed endp, this is uframe index, and not used for full/low speed endp, instead,
+ // iso_packet_desc.param.start_uframe is used.
+ LONG int_start_frame; // frame( ms ) index for high/full/low speed endp
+ struct
+ {
+ UCHAR ctrl_req_flags;
+ UCHAR ctrl_stack_count;
+ UCHAR ctrl_cur_stack; // the receiver uses this by increment the stack pointer first. if the requester
+ UCHAR ctrl_reserved; // send it down with ctrl_stack_count zero, that means the stack is not initialized,
+ // and can be initialized by receiver to 1 and only 1.
+ // If the initializer found the stack size won't meet the number down the drivers, it must
+ // reallocate one urb with the required stack size. and store the previous urb in
+ // ctrl_parent_urb
+ } ctrl_req_context;
+ };
+
+ union
+ {
+ LONG iso_frame_count; // uframe for high speed and frame for full/low speed
+ struct _URB* ctrl_parent_urb;
+ };
+
+ union
+ {
+ ISO_PACKET_DESC iso_packet_desc[ 1 ]; //used to build up trasac_list for iso transfer and claim bandwidth
+ CTRL_REQ_STACK ctrl_req_stack[ 1 ];
+ };
+
+} URB, *PURB;
+
+
+NTSTATUS
+usb_set_dev_ext(
+ULONG dev_ref,
+PVOID dev_ext,
+LONG size
+);
+
+NTSTATUS
+usb_set_if_ext(
+ULONG dev_ref,
+ULONG if_ref,
+PVOID if_ext,
+LONG size
+);
+
+PVOID
+usb_get_dev_ext(
+ULONG dev_ref
+);
+
+PVOID
+usb_get_if_ext(
+ULONG dev_ref,
+ULONG if_ref
+);
+
+NTSTATUS
+usb_claim_interface(
+ULONG dev_ref,
+ULONG if_ref,
+struct _USB_DRIVER *usb_drvr
+);
+
+//return reference to the endp
+ULONG
+usb_get_endp(
+ULONG dev_ref,
+LONG endp_addr
+);
+
+//return reference to the interface
+ULONG
+usb_get_interface(
+ULONG dev_ref,
+LONG if_idx
+);
+
+NTSTATUS
+usb_set_configuration(
+ULONG dev_ref,
+LONG config_value
+);
+
+// each call will return full size of the config desc and
+// its if, endp descs.
+// return value is the bytes actually returned.
+// if the return value is equal to wTotalLength, all the descs of the
+// configuration returned.
+NTSTATUS
+usb_get_config_desc(
+ULONG dev_ref,
+LONG config_idx,
+PCHAR buffer,
+PLONG psize
+);
+
+NTSTATUS
+usb_submit_urb(
+struct _USB_DEV_MANAGER* dev_mgr,
+PURB purb
+);
+
+NTSTATUS
+usb_cancel_urb(
+ULONG dev_ref,
+ULONG endp_ref,
+PURB purb
+);
+
+LONG
+usb_calc_bus_time(
+LONG low_speed,
+LONG input_dir,
+LONG isoc,
+LONG bytecount
+);
+
+//increment the dev->ref_count to lock the dev
+NTSTATUS
+usb_query_and_lock_dev(
+struct _USB_DEV_MANAGER* dev_mgr,
+DEV_HANDLE dev_handle,
+struct _USB_DEV** ppdev
+);
+
+//decrement the dev->ref_count
+NTSTATUS
+usb_unlock_dev(
+struct _USB_DEV *dev
+);
+
+NTSTATUS
+usb_reset_pipe(
+struct _USB_DEV *pdev,
+struct _USB_ENDPOINT *pendp,
+PURBCOMPLETION client_completion,
+PVOID param //parameter for client_completion
+);
+
+VOID
+usb_reset_pipe_completion(
+PURB purb,
+PVOID pcontext
+);
+
+PVOID
+usb_alloc_mem(
+POOL_TYPE pool_type,
+LONG size
+);
+
+VOID
+usb_free_mem(
+PVOID pbuf
+);
+
+PUSB_CONFIGURATION_DESC
+usb_find_config_desc_by_val(
+PUCHAR pbuf,
+LONG val,
+LONG cfg_count
+);
+
+PUSB_CONFIGURATION_DESC
+usb_find_config_desc_by_idx(
+PUCHAR pbuf,
+LONG idx,
+LONG cfg_count
+);
+
+ULONG
+usb_skip_if_and_altif(
+PUCHAR* pdesc_BUF
+);
+
+ULONG
+usb_skip_one_config(
+PUCHAR* pconfig_desc_BUF
+);
+
+VOID
+usb_wait_ms_dpc(
+ULONG ms
+);
+
+VOID
+usb_wait_us_dpc(
+ULONG us
+);
+
+ULONG
+usb_query_clicks(
+PLARGE_INTEGER clicks
+);
+
+VOID
+usb_cal_cpu_freq();
+
+NTSTATUS
+usb_reset_pipe_ex(
+struct _USB_DEV_MANAGER *dev_mgr,
+DEV_HANDLE endp_handle,
+PURBCOMPLETION reset_completion,
+PVOID param
+);
+
+VOID
+usb_call_ctrl_completion(
+PURB purb
+);
+
+BOOL
+is_header_match(
+PUCHAR pbuf,
+ULONG type
+); //used to check descriptor validity
+#endif
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild
new file mode 100644
index 00000000000..6cd0cee3f8b
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild
@@ -0,0 +1,19 @@
+
+
+ .
+ ntoskrnl
+ hal
+ ehci.c
+ uhci.c
+ hub.c
+ td.c
+ usb.c
+ umss.c
+ bulkonly.c
+ cbi.c
+ dmgrdisp.c
+ compdrv.c
+ etd.c
+ gendrv.c
+ ehci.rc
+
diff --git a/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc
new file mode 100644
index 00000000000..d5153dcf41d
--- /dev/null
+++ b/reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc
@@ -0,0 +1,45 @@
+// Resource script for USBISO driver
+// Generated by Walt Oney's driver wizard
+
+#include
+
+LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,1,0
+ PRODUCTVERSION 0,0,1,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "This is a beta version of usb driver stack( ehci ), contact me at mypublic99@yahoo.com\0"
+ VALUE "CompanyName", "Woodhead Software\0"
+ VALUE "FileDescription", "ehci.sys\0"
+ VALUE "FileVersion", "0, 0, 1, 0\0"
+ VALUE "InternalName", "ehci.sys\0"
+ VALUE "LegalCopyright", "Copyright © 2002-2004 Woodhead Software\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "ehci.sys\0"
+ VALUE "PrivateBuild", "0.01\0"
+ VALUE "ProductName", "usb driver stack for windows NT\0"
+ VALUE "ProductVersion", "0, 0, 1, 0\0"
+ VALUE "SpecialBuild", "0131.d\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+