From 430d3e90b8eb4e27bd9566f790a368eb0838d0c6 Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Fri, 18 Aug 2006 20:37:55 +0000 Subject: [PATCH] Import "USB Driver stack project for Windows NT 4.0" by Zhiming/Woodhead (mypublic99@yahoo.com). It supports UHCI/EHCI controllers. Don't expect it to compile with ReactOS build environment. I intend to do some work on it, and use this place to keep my changes, before it's suitable for ReactOS. "I would like to see it useful for ReactOS" (c) woodhead svn path=/trunk/; revision=23602 --- reactos/drivers/usb/directory.rbuild | 3 + .../drivers/usb/nt4compat/directory.rbuild | 3 + .../usb/nt4compat/usbdriver/bulkonly.c | 909 +++ reactos/drivers/usb/nt4compat/usbdriver/cbi.c | 363 + .../drivers/usb/nt4compat/usbdriver/compdrv.c | 566 ++ .../drivers/usb/nt4compat/usbdriver/debug.h | 70 + .../usb/nt4compat/usbdriver/dmgrdisp.c | 551 ++ .../drivers/usb/nt4compat/usbdriver/ehci.c | 6733 +++++++++++++++++ .../drivers/usb/nt4compat/usbdriver/ehci.h | 810 ++ .../drivers/usb/nt4compat/usbdriver/ehci.rc | 45 + reactos/drivers/usb/nt4compat/usbdriver/etd.c | 733 ++ .../drivers/usb/nt4compat/usbdriver/gendrv.c | 2076 +++++ .../drivers/usb/nt4compat/usbdriver/gendrv.h | 65 + reactos/drivers/usb/nt4compat/usbdriver/hcd.h | 71 + reactos/drivers/usb/nt4compat/usbdriver/hub.c | 5184 +++++++++++++ reactos/drivers/usb/nt4compat/usbdriver/hub.h | 794 ++ reactos/drivers/usb/nt4compat/usbdriver/td.c | 687 ++ reactos/drivers/usb/nt4compat/usbdriver/td.h | 1031 +++ .../drivers/usb/nt4compat/usbdriver/uhci.c | 4222 +++++++++++ .../drivers/usb/nt4compat/usbdriver/uhciver.h | 13 + .../drivers/usb/nt4compat/usbdriver/umss.c | 2443 ++++++ .../drivers/usb/nt4compat/usbdriver/umss.h | 353 + reactos/drivers/usb/nt4compat/usbdriver/usb.c | 1518 ++++ reactos/drivers/usb/nt4compat/usbdriver/usb.h | 1062 +++ .../usb/nt4compat/usbdriver/usbdriver.rbuild | 19 + .../usb/nt4compat/usbdriver/usbdriver.rc | 45 + 26 files changed, 30369 insertions(+) create mode 100644 reactos/drivers/usb/nt4compat/directory.rbuild create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/bulkonly.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/cbi.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/compdrv.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/debug.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/dmgrdisp.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/ehci.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/ehci.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/ehci.rc create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/etd.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/gendrv.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/gendrv.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/hcd.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/hub.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/hub.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/td.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/td.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/uhci.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/uhciver.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/umss.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/umss.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/usb.c create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/usb.h create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rbuild create mode 100644 reactos/drivers/usb/nt4compat/usbdriver/usbdriver.rc 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 +